Phase1:

Proplem:

We live in a time when video games are extremely popular. The global video game market continues to grow year-on-year, and the industry is now valued at over $100 billion worldwide. With technology continuously pushing the boundaries, video games have only become more popular and more high-quality. Gameplay mechanics, cutting-edge graphics, and intricate storylines make today’s games more immersive than ever before. We chose this dataset to gain insights on the popularity of upcoming games.

Class label:

Popular’ is our class label, we will use Global_Sales attribute to predict whether a game will sell 1000000 or more globally.

Data Mining Task:

Our data mining task is to predict the popularity of upcoming games using regression.

Description of the dataset:

The dataset provided by vgchartz.com supply us with a valuable resource to explore the platforms and genres of the top 16599 global video games. Through it, we can analyze the most popular platforms and genres that are influencing global sales, and detectr how regions’ sales affect global sales.

Our goal:

Our goal from studying this dataset is to utilize regression techniques on the input data to make predictions about the popularity of upcoming games.

Attributes description:

Attributes name Description Data type
Rank Ranking of the game based on global sales. Numeric
Name Name of the game. Nominal
Platform Platform the game was released on. Nominal
Year Year the game was released. Ordinal
Genre Genre of the game Nominal
Publisher Publisher of the game. Nominal
NA_Sales Sales of the game in North America Numeric (ratio-scaled)
EU_Sales Sales of the game in Europe Numeric (ratio-scaled)
JP_Sales Sales of the game in Japan Numeric (ratio-scaled)
Other_Sales Sales of the game in other regions Numeric (ratio-scaled)
Global_Sales Total sales of the game worldwide Numeric (ratio-scaled)

loading libraries needed for our data mining tasks:

library(outliers) 
library(dplyr)
library(Hmisc)
library(ggplot2)
library(mlbench)
library(caret)
options(max.print=9999999)

Importing our dataset:

dataset=read.csv("Dataset/vgsales.csv")

General info about our dataset:

checking number of rows and columns, and cheking dimensionality and coulumns names:

nrow(dataset)
[1] 16598
ncol(dataset)
[1] 11
dim(dataset)
[1] 16598    11
names(dataset)
 [1] "Rank"         "Name"         "Platform"     "Year"         "Genre"        "Publisher"    "NA_Sales"     "EU_Sales"     "JP_Sales"    
[10] "Other_Sales"  "Global_Sales"

Dataset structure:

str(dataset)
'data.frame':   16598 obs. of  11 variables:
 $ Rank        : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Name        : chr  "Wii Sports" "Super Mario Bros." "Mario Kart Wii" "Wii Sports Resort" ...
 $ Platform    : chr  "Wii" "NES" "Wii" "Wii" ...
 $ Year        : chr  "2006" "1985" "2008" "2009" ...
 $ Genre       : chr  "Sports" "Platform" "Racing" "Sports" ...
 $ Publisher   : chr  "Nintendo" "Nintendo" "Nintendo" "Nintendo" ...
 $ NA_Sales    : num  41.5 29.1 15.8 15.8 11.3 ...
 $ EU_Sales    : num  29.02 3.58 12.88 11.01 8.89 ...
 $ JP_Sales    : num  3.77 6.81 3.79 3.28 10.22 ...
 $ Other_Sales : num  8.46 0.77 3.31 2.96 1 0.58 2.9 2.85 2.26 0.47 ...
 $ Global_Sales: num  82.7 40.2 35.8 33 31.4 ...

sample of raw dataset(first 10 rows):

head(dataset, 10)

sample of raw dataset(last 10 rows):

tail(dataset, 10)

Five number summary of each attribute in our dataset:

summary(dataset)
      Rank           Name             Platform             Year              Genre            Publisher            NA_Sales          EU_Sales      
 Min.   :    1   Length:16598       Length:16598       Length:16598       Length:16598       Length:16598       Min.   : 0.0000   Min.   : 0.0000  
 1st Qu.: 4151   Class :character   Class :character   Class :character   Class :character   Class :character   1st Qu.: 0.0000   1st Qu.: 0.0000  
 Median : 8300   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median : 0.0800   Median : 0.0200  
 Mean   : 8301                                                                                                  Mean   : 0.2647   Mean   : 0.1467  
 3rd Qu.:12450                                                                                                  3rd Qu.: 0.2400   3rd Qu.: 0.1100  
 Max.   :16600                                                                                                  Max.   :41.4900   Max.   :29.0200  
    JP_Sales         Other_Sales        Global_Sales    
 Min.   : 0.00000   Min.   : 0.00000   Min.   : 0.0100  
 1st Qu.: 0.00000   1st Qu.: 0.00000   1st Qu.: 0.0600  
 Median : 0.00000   Median : 0.01000   Median : 0.1700  
 Mean   : 0.07778   Mean   : 0.04806   Mean   : 0.5374  
 3rd Qu.: 0.04000   3rd Qu.: 0.04000   3rd Qu.: 0.4700  
 Max.   :10.22000   Max.   :10.57000   Max.   :82.7400  

variance of numeric data:

var(dataset$NA_Sales)
[1] 0.6669712
var(dataset$EU_Sales)
[1] 0.2553799
var(dataset$JP_Sales)
[1] 0.0956607
var(dataset$Other_Sales)
[1] 0.03556559
var(dataset$Global_Sales)
[1] 2.418112

Graphs:

dataset2 <- dataset %>% sample_n(50)
tab <- dataset2$Platform %>% table()
precentages <- tab %>% prop.table() %>% round(3) * 100 
txt <- paste0(names(tab), '\n', precentages, '%') 

pie(tab, labels=txt , main = "Pie chart of Platform") 

This pie chart illustrate platforms of global video games , We notice from the pie chart of platform attribute that releasing a game for PS users will increase the popularity of the game since it is the most common platform among gamers.

# coloring barplot and adding text
tab<-dataset$Genre %>% table() 

precentages<-tab %>% prop.table() %>% round(3)*100 

txt<-paste0(names(tab), '\n',precentages,'%') 

bb <- dataset$Genre %>% table() %>% barplot(axisnames=F, main = "Barplot for Popular genres ",ylab='count',col=c('pink','blue','lightblue','green','lightgreen','red','orange','red','grey','yellow','azure','olivedrab')) 

text(bb,tab/2,labels=txt,cex=1.5) 

This barplot illustrates popularity of global video games genres ,In terms of genre, action games are the most popular, followed by sports and music games. It is safe to assume that a high number of genres of this nature exist due to their popularity and sales.

boxplot(dataset$NA_Sales , main="
BoxPlot for NA_Sales")

The boxplot of the NA_Sales (Sales of the game in north America) attribute indicates that the values are close to each other ,and there are a lot of outliers since the dataset represents all the north America sales of video games.

boxplot(dataset$EU_Sales, main="
 BoxPlot for EU_Sales")

The boxplot of the EU_Sales (sales of the game in Europe) attribute indicates that the values are close to each other, and there are a lot of outliers since the dataset represents all the Europe sales of video games.

boxplot(dataset$JP_Sales , main="
 BoxPlot for JP_Sales")

The boxplot of the JP_Sales (sales of the game in Japan) attribute indicates that the values are close to each other, and there are a lot of outliers since the dataset represents all the Japan sales of video games.

boxplot(dataset$Other_Sales , main="
 BoxPlot for Other_Sales") 

The boxplot of the Other-sales attribute indicate that the values are close to each other ,and there is a lot of outliers since the dataset represents the global sales of video games.

boxplot(dataset$Global_Sales , main="BoxPlot for Global_Sales")

The boxplot of the Global-sales attribute indicate that the values are close to each other ,and there is a lot of outliers since the dataset represents the global sales of video games.

qplot(data = dataset, x=Global_Sales,y=Genre,fill=I("yellow"),width=0.5 ,geom = "boxplot" , main = "BoxPlots for genre and Global_Sales")

In the boxplot we can see that all the genres have Glob_ sales close to each other, but we notice an outlier that reaches more than 80 Glob_ sales which is a game with genre sports.

dataset$Year %>% table() %>% barplot( main = "Barplot for year")

The barplot of year illustrate that the number of video games were low from 1980 until 2000 , then number of games increased to more than 1200 till 2012.

pairs(~NA_Sales + EU_Sales + JP_Sales + Other_Sales + Global_Sales, data = dataset,
      main = "Sales Scatterplot")

We used Scatterplot to determine the type of correlation we have between the sales; we can see that the majority have positive correlation with each other.

(Pre - processing):

Varaible transformation:

dataset$Rank=as.character(dataset$Rank)

We transformed the Rank from numric to char,because we will use them as ordinal data.

Null checking:

we checked nulls values to know how many nulls values we have, so we can determine how we will deal with them.

sum(is.na(dataset$Rank))
[1] 0
NullRank<-dataset[dataset$Rank=="N/A",]
NullRank

checking for nulls in Rank (there is no nulls)

sum(is.na(dataset$Name))
[1] 0
NullName<-dataset[dataset$Name=="N/A",]
NullName

checking for nulls in name (there is no nulls)

sum(is.na(dataset$Platform))
[1] 0
NullPlatform<-dataset[dataset$Platform=="N/A",]

checking for nulls in Platform(there is no nulls)

sum(is.na(dataset$Year))
[1] 0
NullYear<-dataset[dataset$Year=="N/A",]
NullYear

checking for nulls in year we won’t delete the null and we will leave them as global constant because we want the sales data out of them.

sum(is.na(dataset$Genre))
[1] 0
NullGenre<-dataset[dataset$Genre=="N/A",]
NullGenre

checking for nulls in Genre(there is no nulls)

sum(is.na(dataset$Publisher))
[1] 0
NullPublisher<-dataset[dataset$Publisher=="N/A",]
NullPublisher

checking for nulls in Publisher. we won’t delete the null and we will leave them as global constant as it is because we want the sales data of them.

sum(is.na(dataset$NA_Sales))
[1] 0
NullNA_Sales<-dataset[dataset$NA_Sales=="N/A",]
NullNA_Sales

checking for nulls in NA_Sales (there is no nulls)

sum(is.na(dataset$EU_Sales))
[1] 0
NullEU_Sales<-dataset[dataset$EU_Sales=="N/A",]
NullEU_Sales

checking for nulls in EU_Sales (there is no nulls)

sum(is.na(dataset$JP_Sales))
[1] 0
NullJP_Sales<-dataset[dataset$JP_Sales=="N/A",]
NullJP_Sales

checking for nulls in JP_Sales (there is no nulls)

sum(is.na(dataset$Other_Sales))
[1] 0
NullOther_Sales<-dataset[dataset$Other_Sales=="N/A",]

There is no null values in the other_sales.

sum(is.na(dataset$Global_Sales))
[1] 0
NullGlobal_Sales<-dataset[dataset$Global_Saless=="N/A",]

There is no null values in the Global_Sales.

Encoding:

We will encode our categorical data since most machine learning algorithms work with numbers rather than text.

dataset$Platform=factor(dataset$Platform,levels=c("2600","3DO","3DS","DC","DS","GB","GBA","GC","GEN","GG","N64","NES","NG","PC","PCFX","PS","PS2","PS3","PS4","PSP","PSV","SAT","SCD","SNES","TG16","Wii","WiiU","WS","X360","XB","XOne"), labels=c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31))

this column will be encoded to facilitate our data mining task.

dataset$Genre=factor(dataset$Genre,levels=c("Action","Adventure","Fighting","Platform","Puzzle","Racing","Role-Playing","Shooter","Simulation","Sports","Strategy","Misc"),labels=c(1,2,3,4,5,6,7,8,9,10,11,12))

Since most machine learning algorithms work with numbers and not with text or categorical variables, this column will be encoded to facilitate our data mining task.

Outliers:

Analyses and statistical models can be ruined by outliers, making it difficult to detect a true effect. Therefore, we are checking for them and removing them if we find any.

outlier of NA_Sales

OutNA_Sales = outlier(dataset$NA_Sales, logical =TRUE)
sum(OutNA_Sales)
[1] 1
Find_outlier = which(OutNA_Sales ==TRUE, arr.ind = TRUE)

outlier of EU_Sales

OutEU_Sales = outlier(dataset$EU_Sales, logical =TRUE)
sum(OutEU_Sales)
[1] 1
Find_outlier = which(OutEU_Sales ==TRUE, arr.ind = TRUE)

outlier of JP_Sales

OutJP_Sales = outlier(dataset$JP_Sales, logical =TRUE)
sum(OutJP_Sales)
[1] 1
Find_outlier = which(OutJP_Sales ==TRUE, arr.ind = TRUE)

outlier of other_sales

OutOS=outlier(dataset$Other_Sales, logical=TRUE)  
sum(OutOS)  
[1] 1
Find_outlier=which(OutOS==TRUE, arr.ind=TRUE)  

outlier of Global_sales

OutGS=outlier(dataset$Global_Sales, logical=TRUE)  
sum(OutGS)  
[1] 1
Find_outlier=which(OutGS==TRUE, arr.ind=TRUE)  

Remove outliers

dataset= dataset[-Find_outlier,]

Normalization:

The normalization of data will improve the performance of many machine learning algorithms by accounting for differences in the scale of the input features.

Dataset before normalization:

datsetWithoutNormalization<-dataset
normalize <- function(x) {return ((x - min(x)) / (max(x) - min(x)))}
dataset$NA_Sales<-normalize(datsetWithoutNormalization$NA_Sales)
dataset$EU_Sales<-normalize(datsetWithoutNormalization$EU_Sales)
dataset$JP_Sales<-normalize(datsetWithoutNormalization$JP_Sales)
dataset$Other_Sales<-normalize(datsetWithoutNormalization$Other_Sales)
dataset$Global_Sales<-normalize(datsetWithoutNormalization$Global_Sales)

We chose min-max normalization instead of z-score normalization because min-max transform the data into a specific range, which enhances its suitability for visualization and comparison. Additionally, it simplifies the process of assessing attribute importance and their contributions to the model.

Feautre selection:

Our class label (popular) refers to Global_Sales.because we have multiple regions sales we chose to evaluate each region sales based on their importance to (global_sales) column,and those that are less important will be deleted from the dataset.

Use roc_curve area as score

roc_imp <- filterVarImp(x = dataset[,7:10], y = dataset$Global_Sales)

Sort the score in decreasing order

roc_imp <- data.frame(cbind(variable = rownames(roc_imp), score = roc_imp[,1]))
roc_imp$score <- as.double(roc_imp$score)
roc_imp[order(roc_imp$score,decreasing = TRUE),]

we will remove the (JP_Sales) because it is of low importance to our class_label(Global_Sales)

dataset<- dataset[,-9]

Dataset balncing and discritization:

#Discritization

dataBeforDiscertize=(dataset[,7:10])
library("arules")
dataAfterDiscertize=discretizeDF(
dataBeforDiscertize)
Warning: The calculated breaks are: 0, 0, 0.0046583850931677, 1
  Only unique breaks are used reducing the number of intervals. Look at ? discretize for details.Warning: The calculated breaks are: 0, 0, 0.00189214758751183, 1
  Only unique breaks are used reducing the number of intervals. Look at ? discretize for details.
unique(dataAfterDiscertize[,1])
[1] [0.0055,1]        [0.000688,0.0055) [0,0.000688)     
Levels: [0,0.000688) [0.000688,0.0055) [0.0055,1]
unique(dataAfterDiscertize[,2])
[1] [0.00466,1] [0,0.00466)
Levels: [0,0.00466) [0.00466,1]
unique(dataAfterDiscertize[,3])
[1] [0.00189,1] [0,0.00189)
Levels: [0,0.00189) [0.00189,1]
unique(dataAfterDiscertize[,4])
[1] [0.00795,1]       [0.00199,0.00795) [0,0.00199)      
Levels: [0,0.00199) [0.00199,0.00795) [0.00795,1]
levels(dataAfterDiscertize$NA_Sales)<-c("low","medium","high")
levels(dataAfterDiscertize$EU_Sales)<-c("low","high")
levels(dataAfterDiscertize$Other_Sales)<-c("low","high")
levels(dataAfterDiscertize$Global_Sales)<-c("low","medium","high")

#Balancing

library(groupdata2)
dataset<-downsample(dataset,cat_col="Global_Sales")

Dataset after pre-processing:

print(dataset)

We performed balancing and discritization because we notice from the graphs that our dataset is inbalanced.

Data Mining Techniques:

Classification:

The goal of classification is to build a model or algorithm that can generalize patterns and relationships observed in the training data to make accurate predictions on unseen data. The model learns from the labeled examples in the training set, where each example consists of a set of input features and a corresponding known class label.

Information gain:

By using Information Gain to select attributes for splitting, the decision tree algorithm aims to create a tree that maximizes the information provided by the attributes about the class labels, leading to effective classification.

Clustring:

Clustering is a technique used to group similar data points together based on their inherent characteristics or similarities. So our goal of clustering is to identify patterns, structures, or relationships within a dataset without any prior knowledge of the groups or classes that may exist.

1:Preprossening before clustring

before starting the clustring process we need to remove the class label since clustring is an unsupervised learning , but before removing the class label We stored it in a varible just in case of further need(We need it to compute Bcubed precision and recall),Then we need to transform each factor coulnm to numeric because it’s essential to convert factor variables to numeric ones due to the algorithmic requirements of clustring(Kmeans) and the characteristics of factor variables.

# We stored the class label in a varible just in case of further need(We need it to compute Bcubed precision and recall)

classLabel<-dataset$Global_Sales
# Removing the classLabel before the clustring process
dataset<- dataset[,-10]
# We removed columns that are not relevant to the clustering process and can distort the result 
datasetClustering <- dataset[, setdiff(3:9, c(4, 6))]
View(datasetClustering)

##converting factors to numric to apply kmeans method , it's essential to convert factor variables to numeric ones due to the algorithmic requirements of K-means and the characteristics of factor variables.

datasetClustering$Platform <- as.numeric(as.character(datasetClustering$Platform))
datasetClustering$Genre <- as.numeric(as.character(datasetClustering$Genre))
View(datasetClustering)

After preprocessing the data now we will start performin the clustring technique on the processsed dataset.

2:Kmeans

We chose K-means clustering as our clustring method because it excels in handling large datasets, offering prompt and easily understandable insights. It is beneficial for exploring data, facilitating the quick detection of potential data clusters.

3:Choosing the number of clusters K

1-Silhouette method

This graph depicts the process of finding the optimal number of clusters for a dataset using the Silhouette method. The x-axis represents the number of clusters (k) considered in the analysis, ranging from 1 to 10. The y-axis shows the average Silhouette width, which is a measure of how similar an object is to its own cluster compared to other clusters.

fviz_nbclust(datasetClustering, kmeans, method = "silhouette")+
  labs(subtitle = "Silhouette method")

The plot shows a peak at k=3, where the average Silhouette score is the highest. This suggests that the data points are, on average, closer to other points in their own cluster and farther from points in other clusters when the data is divided into three clusters. As a result, according to the Silhouette method, k=3 is the optimal number of clusters.

2- Elbow method

The Elbow Method using Within-Cluster Sum of Squares (WSS) is a technique to determine the optimal number of clusters in K-means clustering. It involves running the clustering algorithm for a range of cluster numbers and calculating the WSS for each. WSS is the sum of squared distances of each point to its cluster centroid. As the number of clusters increases, WSS tends to decrease; the goal is to find the point where increasing the number of clusters does not lead to a significant decrease in WSS. This point, visually resembling an elbow on a plot of WSS against the number of clusters, is considered the optimal number of clusters.

fviz_nbclust(datasetClustering, kmeans, method = "wss") +
  geom_vline(xintercept = 4, linetype = 2)+
  labs(subtitle = "Elbow method")

As shown in the above graph , 4 is the value that resembles an elbow in the plot(The turnning point) wich means it is the optimal value of K the we will use in our clustring process.

In conclusion, we will choose K=4 for our clustering process, as it marks the turning point on the Elbow Method curve, indicating an optimal balance in cluster compactness and separation. Additionally, we will utilize K=3 and K=6, as these values maximize the average silhouette width, with K=3 being the primary maximizer and K=6 the secondary. By selecting these specific K values, we aim to achieve a satisfactory level of precision and recall in our clustering analysis, ensuring both the relevance and completeness of the clustered data.

k-means clustering, visualization and evaluation

1- k=3

set.seed(5000)
kmeans.result <- kmeans(datasetClustering, 3)

# print the clusterng result
kmeans.result
K-means clustering with 3 clusters of sizes 153, 316, 153

Cluster means:
   Platform    Genre   NA_Sales   EU_Sales Other_Sales
1  5.830065 5.562092 0.08035754 0.10050542  0.02601394
2 17.003165 5.471519 0.06264909 0.09720939  0.04795037
3 27.967320 6.509804 0.10228488 0.12072220  0.04096561

Clustering vector:
  [1] 2 1 1 1 1 3 3 2 3 1 3 3 3 2 2 2 2 1 2 1 3 2 1 2 2 2 2 1 2 1 2 2 2 3 2 3 1 2 3 1 3 1 1 3 2 3 2 2 2 2 3 1 3 2 2 2 3 1 3 1 1 1 2 3 1 2 2 3 2 1 3 2
 [73] 2 2 1 2 1 2 2 1 2 2 3 2 2 1 2 3 1 3 1 2 3 3 1 2 3 2 2 2 2 3 2 3 3 1 3 3 2 1 1 2 3 2 2 2 2 2 3 2 3 2 1 2 2 1 2 1 1 1 3 1 2 1 1 3 2 1 2 2 2 1 1 2
[145] 2 2 2 1 1 3 3 2 2 3 2 2 2 1 2 1 1 3 2 1 3 1 2 1 2 2 2 1 3 2 2 1 2 2 2 3 2 1 1 2 2 1 2 2 1 2 3 2 2 2 2 2 2 2 1 2 1 2 2 2 2 2 2 2 2 3 2 1 1 2 2 2
[217] 1 3 3 1 3 3 2 1 2 2 1 2 2 1 2 1 3 3 1 1 3 2 1 2 2 2 2 2 2 3 1 2 3 2 2 2 1 1 2 3 3 2 1 2 3 2 3 2 3 2 1 3 2 3 3 2 2 3 1 2 2 2 2 2 1 2 2 2 3 2 3 2
[289] 3 3 3 3 2 3 1 2 1 2 2 3 2 3 3 1 2 2 3 2 2 2 1 1 3 2 2 2 2 2 2 2 1 2 3 2 3 2 3 2 2 2 2 1 2 2 3 2 3 1 3 2 3 2 3 2 2 2 2 1 2 2 2 2 1 2 1 2 1 2 2 2
[361] 2 2 3 2 1 3 1 2 2 2 2 2 2 1 2 2 3 1 2 2 1 2 3 2 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 3 1 2 1 2 2 2 3 2 3 3 2 2 2 2 1 2 2 3 1 1 3 3 2 3 2 3 2 2 3 2 2 2
[433] 2 2 2 2 1 1 2 2 1 2 2 2 2 2 1 2 2 2 3 1 1 1 3 3 1 2 2 2 1 3 2 2 1 1 2 2 2 1 1 1 1 2 3 2 3 2 2 2 1 2 2 3 1 2 2 2 2 2 3 3 2 2 3 1 3 3 1 2 1 3 2 2
[505] 2 2 2 1 2 2 2 3 3 2 2 2 3 1 3 1 2 2 2 2 3 3 3 3 3 1 2 2 2 2 3 2 1 1 2 2 3 1 2 2 1 3 3 3 3 1 1 3 3 3 2 3 2 2 3 1 1 3 3 3 1 1 3 2 2 2 1 2 3 1 1 3
[577] 2 1 1 2 3 1 1 2 3 2 2 3 3 2 2 1 1 3 2 1 1 1 2 3 2 1 1 1 3 2 2 3 3 3 1 1 1 2 3 3 1 1 1 3 3 2

Within cluster sum of squares by cluster:
[1] 2398.331 5091.704 2840.916
 (between_SS / total_SS =  78.5 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"         "ifault"      
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = datasetClustering)

#average silhouette for cluster  k=3
library(cluster)
avg_sil <- silhouette(kmeans.result$cluster,dist(datasetClustering))
fviz_silhouette(avg_sil)

#Within-cluster sum of squares wss 
wss <- kmeans.result$tot.withinss
print(wss)
[1] 10330.95
#BCubed
kmeans_cluster <- c(kmeans.result$cluster)

ground_truth <- c(classLabel)

data <- data.frame(cluster = kmeans_cluster, label = ground_truth)

# Function to calculate BCubed precision and recall
  bcubed <- function(data) {
  n <- nrow(data)
  total_precesion <- 0
  total_recall <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Count the number of items from the same category within the same cluster
intersection <- sum(data$label[data$cluster == cluster] == label)
    
# Count the total number of items in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Count the total number of items with the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
total_precesion <- total_precesion + intersection /total_same_cluster
total_recall <- total_recall + intersection / total_same_category
  }

  # Calculate average precision and recall
  precision <- total_precesion / n
  recall <- total_recall / n

  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- bcubed(data)

# Extract precision and recall from the metrics
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.004823151 
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 1 

As the graph of K=3 illustrated , there is a noticeable overlapping between the clusters that effect the cluster performance duo to the similarity between clusters and wide distance in the cluster itself as the high value of wss indicate(77%) ,the recall value is acceptable which is 0.39 , the value of precision 0.021 is low which can be duo to the presence of outliers ,the value of average silhouette width is 0.52 relatively good for the clustering process, Overall, the plot suggests that dividing the data into 3 clusters seems appropriate because the average Silhouette score is relativley high.

1- k=4

set.seed(5000)
kmeans.result <- kmeans(datasetClustering, 4)

# print the clusterng result
kmeans.result
K-means clustering with 4 clusters of sizes 141, 178, 153, 150

Cluster means:
   Platform    Genre   NA_Sales   EU_Sales Other_Sales
1 16.936170 1.879433 0.07084150 0.09965971  0.04933003
2 16.955056 8.387640 0.05618750 0.09439772  0.04611312
3 27.967320 6.509804 0.10228488 0.12072220  0.04096561
4  5.726667 5.480000 0.08067859 0.10160455  0.02645853

Clustering vector:
  [1] 1 4 4 4 4 3 3 2 3 4 3 3 3 2 2 1 1 4 1 4 3 2 4 2 2 1 2 4 2 4 2 2 1 3 1 3 4 2 3 4 3 4 4 3 2 3 2 2 2 2 3 4 3 2 2 2 3 4 3 4 4 4 2 3 4 2 1 3 2 4 3 2
 [73] 2 1 4 1 4 2 2 4 2 2 3 1 1 4 2 3 4 3 4 2 3 3 4 2 3 1 2 2 1 3 2 3 3 4 3 3 1 4 4 2 3 1 2 2 2 1 3 2 3 1 4 1 1 4 1 4 4 4 3 4 2 4 4 3 2 4 2 2 2 4 4 2
[145] 1 1 1 4 4 3 3 1 2 3 1 1 2 4 1 4 4 3 1 4 3 4 2 4 1 2 2 4 3 2 2 4 1 2 1 3 2 4 2 2 1 4 2 1 4 2 3 2 2 2 2 2 2 1 4 1 4 1 2 1 2 2 2 2 1 3 1 4 4 2 2 2
[217] 2 3 3 4 3 3 1 4 1 2 4 2 1 4 2 4 3 3 4 4 3 1 4 2 2 2 2 2 2 3 4 2 3 2 1 2 4 4 2 3 3 2 4 1 3 2 3 1 3 2 4 3 1 3 3 1 1 3 4 2 1 1 1 2 4 1 1 1 3 2 3 2
[289] 3 3 3 3 2 3 4 2 4 1 1 3 1 3 3 4 2 1 3 1 1 2 4 4 3 1 1 1 2 2 1 1 4 1 3 2 3 1 3 1 1 2 2 4 1 2 3 2 3 4 3 1 3 1 3 1 1 1 2 4 2 1 2 2 4 2 4 2 4 1 1 2
[361] 2 1 3 1 4 3 4 2 2 1 2 1 2 4 1 2 3 4 2 2 4 1 3 1 3 3 3 2 2 1 1 1 1 2 2 1 1 1 1 3 4 2 4 2 2 1 3 2 3 3 1 1 1 2 4 2 1 3 4 4 3 3 1 3 1 3 2 1 3 2 2 2
[433] 1 1 1 2 4 4 2 1 4 2 1 2 2 1 4 1 1 2 3 4 4 4 3 3 4 2 2 1 4 3 2 2 4 4 2 2 2 2 4 4 4 2 3 1 3 1 1 2 4 1 1 3 4 1 2 1 1 1 3 3 1 2 3 4 3 3 4 2 4 3 1 1
[505] 2 2 1 4 2 2 1 3 3 1 2 2 3 4 3 4 1 1 2 2 3 3 3 3 3 4 1 1 2 2 3 1 4 4 2 2 3 4 2 1 4 3 3 3 3 4 4 3 3 3 2 3 2 2 3 4 4 3 3 3 4 4 3 1 2 2 4 2 3 4 4 3
[577] 2 4 4 1 3 4 4 2 3 1 2 3 3 2 2 4 4 3 2 4 4 4 1 3 1 4 4 4 3 1 1 3 3 3 4 4 4 2 3 3 4 4 4 3 3 1

Within cluster sum of squares by cluster:
[1]  729.254 1191.980 2840.916 2262.303
 (between_SS / total_SS =  85.3 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"         "ifault"      
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = datasetClustering)

#average silhouette for cluster  k=4
library(cluster)
avg_sil <- silhouette(kmeans.result$cluster,dist(datasetClustering))
fviz_silhouette(avg_sil)

#Within-cluster sum of squares wss 
wss <- kmeans.result$tot.withinss
print(wss)
[1] 7024.453
#BCubed
kmeans_cluster <- c(kmeans.result$cluster)

ground_truth <- c(classLabel)

data <- data.frame(cluster = kmeans_cluster, label = ground_truth)

# Function to calculate BCubed precision and recall
  bcubed <- function(data) {
  n <- nrow(data)
  total_precesion <- 0
  total_recall <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Count the number of items from the same category within the same cluster
intersection <- sum(data$label[data$cluster == cluster] == label)
    
# Count the total number of items in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Count the total number of items with the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
total_precesion <- total_precesion + intersection /total_same_cluster
total_recall <- total_recall + intersection / total_same_category
  }

  # Calculate average precision and recall
  precision <- total_precesion / n
  recall <- total_recall / n

  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- bcubed(data)

# Extract precision and recall from the metrics
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.006430868 
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 1 

As the graph of K=4 illustrated , there is a noticeable overlapping between the clusters that effect the cluster performance duo to the similarity between clusters and some wide distance in the cluster itself as the high value of wss indicate(83%) ,the recall value is acceptable which is 0.28 but less than K=3 recall value , the value of precision 0.025 is low which can be duo to the presence of outliers ,the value of average silhouette width is 0.52 which is relatively good for the clustering process, Overall, the plot suggests that dividing the data into 4 clusters seems appropriate but it would better if choose k=3 because it has lower wss value and higher precision and recall .

1- k=6

set.seed(5000)
kmeans.result <- kmeans(datasetClustering, 6)

# print the clusterng result
kmeans.result
K-means clustering with 6 clusters of sizes 41, 155, 97, 138, 135, 56

Cluster means:
   Platform    Genre   NA_Sales   EU_Sales Other_Sales
1 12.097561 7.634146 0.11401684 0.08847144  0.01571405
2 17.516129 8.341935 0.05254249 0.09515127  0.05041658
3 28.123711 9.092784 0.11251223 0.13506916  0.04713788
4  5.347826 5.326087 0.07978490 0.10445922  0.02807371
5 17.155556 1.770370 0.06120536 0.09855072  0.05036617
6 27.696429 2.035714 0.08456966 0.09587123  0.03027436

Clustering vector:
  [1] 5 4 4 4 4 3 3 1 6 4 3 3 6 2 2 5 5 1 5 4 3 2 4 2 2 5 2 4 2 4 2 1 5 6 5 3 4 2 6 4 6 4 4 6 2 3 2 2 2 2 3 4 3 2 2 2 3 4 6 4 4 4 2 3 4 2 5 6 2 4 3 2
 [73] 1 5 4 5 4 2 2 4 2 2 3 5 5 4 2 3 4 6 4 2 3 6 4 2 6 5 2 2 5 6 2 6 3 4 6 6 5 4 1 2 3 5 1 2 1 5 6 2 6 5 4 5 5 4 5 4 4 4 3 4 2 4 4 3 2 1 2 2 2 4 4 2
[145] 5 5 5 1 4 6 3 5 2 6 5 1 2 4 5 4 4 3 5 4 3 4 2 4 5 2 2 4 6 2 2 4 5 2 5 3 2 4 1 2 5 4 2 5 4 2 3 1 2 2 2 1 2 5 4 5 4 5 2 5 2 2 2 1 5 6 5 4 4 1 2 1
[217] 1 3 6 4 3 6 5 4 5 2 4 2 5 4 2 4 3 3 4 4 3 5 4 1 2 2 2 2 2 3 1 2 6 2 5 1 4 4 2 3 6 2 4 5 3 2 6 5 3 2 4 3 5 3 6 5 5 3 4 2 5 5 5 2 4 5 5 5 3 2 3 2
[289] 3 3 6 3 2 6 4 1 4 5 5 6 5 3 6 4 1 5 3 5 5 2 1 4 6 5 5 5 2 2 5 5 4 5 3 2 3 5 6 5 5 2 2 4 5 2 3 2 6 4 3 5 6 5 3 5 5 5 2 4 2 5 2 2 1 2 4 2 4 5 5 2
[361] 1 5 6 5 4 3 4 2 2 5 2 5 2 4 5 2 3 4 2 2 1 5 3 5 3 3 6 2 1 5 5 5 5 2 2 5 5 5 5 3 4 2 4 2 2 5 3 2 3 3 5 5 5 2 4 2 5 3 4 4 6 3 5 3 5 3 2 5 6 2 2 2
[433] 5 5 1 2 1 4 2 5 4 2 5 2 2 5 4 5 5 1 3 4 4 4 6 6 4 2 2 5 4 6 2 2 4 4 2 2 2 1 4 4 4 2 3 5 6 5 1 2 4 5 5 6 4 5 2 5 5 5 3 3 5 1 6 4 6 3 4 2 4 3 5 5
[505] 2 2 5 4 2 2 5 3 3 5 2 2 3 4 3 4 5 5 2 2 3 3 6 3 3 4 1 5 2 2 6 5 4 4 2 2 3 1 1 5 4 3 3 3 3 4 4 3 6 3 2 3 2 2 3 4 1 3 3 3 4 4 6 5 2 2 4 2 6 4 4 6
[577] 2 1 4 5 3 4 4 2 6 5 2 3 3 2 2 4 4 3 2 4 4 4 5 6 1 4 4 4 6 5 5 3 3 3 4 4 4 1 6 3 4 4 4 3 3 1

Within cluster sum of squares by cluster:
[1]  361.4029  726.4409  697.2027 1868.5574  536.4539  368.9975
 (between_SS / total_SS =  90.5 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"         "ifault"      
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = datasetClustering)

#average silhouette for cluster  k=6
library(cluster)
avg_sil <- silhouette(kmeans.result$cluster,dist(datasetClustering))
fviz_silhouette(avg_sil)

#Within-cluster sum of squares wss 
wss <- kmeans.result$tot.withinss
print(wss)
[1] 4559.055
#BCubed
kmeans_cluster <- c(kmeans.result$cluster)

ground_truth <- c(classLabel)

data <- data.frame(cluster = kmeans_cluster, label = ground_truth)

# Function to calculate BCubed precision and recall
  bcubed <- function(data) {
  n <- nrow(data)
  total_precesion <- 0
  total_recall <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Count the number of items from the same category within the same cluster
intersection <- sum(data$label[data$cluster == cluster] == label)
    
# Count the total number of items in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Count the total number of items with the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
total_precesion <- total_precesion + intersection /total_same_cluster
total_recall <- total_recall + intersection / total_same_category
  }

  # Calculate average precision and recall
  precision <- total_precesion / n
  recall <- total_recall / n

  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- bcubed(data)

# Extract precision and recall from the metrics
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.009646302 
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 1 
Measure K=3 K=4 K=6
Average Silhouette width 0.55 0.54 0.52
Total within-cluster sum of square 10330.95 7024.453 4559.055
BCubed precision 0.00482315 0.00643087 0.00964630
BCubed recall 1.00 1.00 1.00

As the graph of K=6 illustrated , there is a noticeable overlapping between the clusters that effect the cluster performance duo to the similarity between clusters and wide distance in the cluster itself as the high value of wss indicate (86%) ,the recall value is acceptable which is 0.22 and it is the lowest value between the other clusters, the value of precision 0.02 is low which can be duo to the presence of outliers, the value of average silhouette width is 0.4 which is bad for the clustering process it. Overall, the plot suggests that dividing the data into 3 clusters seems appropriate because the average Silhouette score is high and the individual data points generally have good Silhouette values.

LS0tCnRpdGxlOiAiR2xvYmFsIHZpZGVvIGdhbWVzIHNhbGVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMgUGhhc2UxOgojIFByb3BsZW06CldlIGxpdmUgaW4gYSB0aW1lIHdoZW4gdmlkZW8gZ2FtZXMgYXJlIGV4dHJlbWVseSBwb3B1bGFyLiBUaGUgZ2xvYmFsIHZpZGVvIGdhbWUgbWFya2V0IGNvbnRpbnVlcyB0byBncm93IHllYXItb24teWVhciwgYW5kIHRoZSBpbmR1c3RyeSBpcyBub3cgdmFsdWVkIGF0IG92ZXIgJDEwMCBiaWxsaW9uIHdvcmxkd2lkZS4gV2l0aCB0ZWNobm9sb2d5IGNvbnRpbnVvdXNseSBwdXNoaW5nIHRoZSBib3VuZGFyaWVzLCB2aWRlbyBnYW1lcyBoYXZlIG9ubHkgYmVjb21lIG1vcmUgcG9wdWxhciBhbmQgbW9yZSBoaWdoLXF1YWxpdHkuIEdhbWVwbGF5IG1lY2hhbmljcywgY3V0dGluZy1lZGdlIGdyYXBoaWNzLCBhbmQgaW50cmljYXRlIHN0b3J5bGluZXMgbWFrZSB0b2RheSdzIGdhbWVzIG1vcmUgaW1tZXJzaXZlIHRoYW4gZXZlciBiZWZvcmUuIFdlIGNob3NlIHRoaXMgZGF0YXNldCB0byBnYWluIGluc2lnaHRzIG9uIHRoZSBwb3B1bGFyaXR5IG9mIHVwY29taW5nIGdhbWVzLiAKCgojIENsYXNzIGxhYmVsOgpQb3B1bGFyJyBpcyBvdXIgY2xhc3MgbGFiZWwsIHdlIHdpbGwgdXNlIEdsb2JhbF9TYWxlcyBhdHRyaWJ1dGUgdG8gcHJlZGljdCB3aGV0aGVyIGEgZ2FtZSB3aWxsIHNlbGwgMTAwMDAwMCBvciBtb3JlIGdsb2JhbGx5LiAKCgojIERhdGEgTWluaW5nIFRhc2s6Ck91ciBkYXRhIG1pbmluZyB0YXNrIGlzIHRvIHByZWRpY3QgdGhlIHBvcHVsYXJpdHkgb2YgdXBjb21pbmcgZ2FtZXMgdXNpbmcgcmVncmVzc2lvbi4KCgojIERlc2NyaXB0aW9uIG9mIHRoZSBkYXRhc2V0OgpUaGUgZGF0YXNldCBwcm92aWRlZCBieSB2Z2NoYXJ0ei5jb20gc3VwcGx5IHVzIHdpdGggYSB2YWx1YWJsZSByZXNvdXJjZSB0byBleHBsb3JlIHRoZSBwbGF0Zm9ybXMgYW5kIGdlbnJlcyBvZiB0aGUgdG9wIDE2NTk5IGdsb2JhbCB2aWRlbyBnYW1lcy4gVGhyb3VnaCBpdCwgd2UgY2FuIGFuYWx5emUgdGhlIG1vc3QgcG9wdWxhciBwbGF0Zm9ybXMgYW5kIGdlbnJlcyB0aGF0IGFyZSBpbmZsdWVuY2luZyBnbG9iYWwgc2FsZXMsIGFuZCBkZXRlY3RyIGhvdyByZWdpb25zJyBzYWxlcyBhZmZlY3QgZ2xvYmFsIHNhbGVzLiAKCiMgT3VyIGdvYWw6CgpPdXIgZ29hbCAgZnJvbSBzdHVkeWluZyB0aGlzIGRhdGFzZXQgaXMgdG8gdXRpbGl6ZSByZWdyZXNzaW9uIHRlY2huaXF1ZXMgb24gdGhlIGlucHV0IGRhdGEgdG8gbWFrZSBwcmVkaWN0aW9ucyBhYm91dCB0aGUgcG9wdWxhcml0eSBvZiB1cGNvbWluZyBnYW1lcy4KCiMgU291cmNlIGFuZCBsaW5rOgpTb3VyY2U6IEthZ2dsZQoKVVJMIGxpbms6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvZ3JlZ29ydXQvdmlkZW9nYW1lc2FsZXMKCgoKCiMgQXR0cmlidXRlcyBkZXNjcmlwdGlvbjoKCgp8ICoqQXR0cmlidXRlcyBuYW1lKiogfCAqKkRlc2NyaXB0aW9uKiogICAgICAgICAgICAgICAgICAgfCAqKkRhdGEgdHlwZSoqIHwgCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfFJhbmsgICAgICAgICAgICAgICB8IFJhbmtpbmcgb2YgdGhlIGdhbWUgYmFzZWQgb24gZ2xvYmFsIHNhbGVzLiB8IE51bWVyaWMgICAgICAgfAp8IE5hbWUgICAgICAgICAgICB8IE5hbWUgb2YgdGhlIGdhbWUuIHwgTm9taW5hbCAgICAgICB8IAp8IFBsYXRmb3JtICAgICAgfCBQbGF0Zm9ybSB0aGUgZ2FtZSB3YXMgcmVsZWFzZWQgb24uIHwgTm9taW5hbCAgICAgICB8IAp8IFllYXIgICAgICAgICAgICAgICB8IFllYXIgdGhlIGdhbWUgd2FzIHJlbGVhc2VkLiB8IE9yZGluYWwgICAgICAgfCAKfCBHZW5yZSAgICAgICAgICAgIHwgR2VucmUgb2YgdGhlIGdhbWUgfCBOb21pbmFsICAgICAgIHwgCnwgUHVibGlzaGVyICAgICAgfCBQdWJsaXNoZXIgb2YgdGhlIGdhbWUuIHwgTm9taW5hbCAgICAgICB8IAp8IE5BX1NhbGVzICAgICAgfCBTYWxlcyBvZiB0aGUgZ2FtZSBpbiBOb3J0aCBBbWVyaWNhIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgICB8IAp8IEVVX1NhbGVzICAgICAgIHwgU2FsZXMgb2YgdGhlIGdhbWUgaW4gRXVyb3BlIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgICAgfCAKfCBKUF9TYWxlcyAgICAgICAgfCBTYWxlcyBvZiB0aGUgZ2FtZSBpbiBKYXBhbiB8IE51bWVyaWMgKHJhdGlvLXNjYWxlZCkgICAgICAgIHwgCnwgT3RoZXJfU2FsZXMgfCBTYWxlcyBvZiB0aGUgZ2FtZSBpbiBvdGhlciByZWdpb25zIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgICAgfCAKfCBHbG9iYWxfU2FsZXMgIHwgVG90YWwgc2FsZXMgb2YgdGhlIGdhbWUgd29ybGR3aWRlIHwgTnVtZXJpYyAocmF0aW8tc2NhbGVkKSAgICAgfCAgICAgCgoKCgoKCiMgbG9hZGluZyBsaWJyYXJpZXMgbmVlZGVkIGZvciBvdXIgZGF0YSBtaW5pbmcgdGFza3M6CmBgYHtyfQpsaWJyYXJ5KG91dGxpZXJzKSAKbGlicmFyeShkcGx5cikKbGlicmFyeShIbWlzYykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KG1sYmVuY2gpCmxpYnJhcnkoY2FyZXQpCm9wdGlvbnMobWF4LnByaW50PTk5OTk5OTkpCmBgYAoKCgoKCiMgSW1wb3J0aW5nIG91ciBkYXRhc2V0OgpgYGB7cn0KZGF0YXNldD1yZWFkLmNzdigiRGF0YXNldC92Z3NhbGVzLmNzdiIpCmBgYAoKCgoKIyBHZW5lcmFsIGluZm8gYWJvdXQgb3VyIGRhdGFzZXQ6CgpjaGVja2luZyBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucywgYW5kIGNoZWtpbmcgZGltZW5zaW9uYWxpdHkgYW5kIGNvdWx1bW5zIG5hbWVzOgpgYGB7cn0KbnJvdyhkYXRhc2V0KQpuY29sKGRhdGFzZXQpCmRpbShkYXRhc2V0KQpuYW1lcyhkYXRhc2V0KQpgYGAKCgoKCkRhdGFzZXQgc3RydWN0dXJlOgpgYGB7cn0Kc3RyKGRhdGFzZXQpCmBgYAoKCgpzYW1wbGUgb2YgcmF3IGRhdGFzZXQoZmlyc3QgMTAgcm93cyk6CmBgYHtyfQpoZWFkKGRhdGFzZXQsIDEwKQpgYGAKCnNhbXBsZSBvZiByYXcgZGF0YXNldChsYXN0IDEwIHJvd3MpOgpgYGB7cn0KdGFpbChkYXRhc2V0LCAxMCkKYGBgCgpGaXZlIG51bWJlciBzdW1tYXJ5IG9mIGVhY2ggYXR0cmlidXRlIGluIG91ciBkYXRhc2V0OgpgYGB7cn0Kc3VtbWFyeShkYXRhc2V0KQpgYGAKCnZhcmlhbmNlIG9mIG51bWVyaWMgZGF0YToKYGBge3J9CnZhcihkYXRhc2V0JE5BX1NhbGVzKQp2YXIoZGF0YXNldCRFVV9TYWxlcykKdmFyKGRhdGFzZXQkSlBfU2FsZXMpCnZhcihkYXRhc2V0JE90aGVyX1NhbGVzKQp2YXIoZGF0YXNldCRHbG9iYWxfU2FsZXMpCmBgYAoKCgoKCgojIEdyYXBoczoKCmBgYHtyfQpkYXRhc2V0MiA8LSBkYXRhc2V0ICU+JSBzYW1wbGVfbig1MCkKdGFiIDwtIGRhdGFzZXQyJFBsYXRmb3JtICU+JSB0YWJsZSgpCnByZWNlbnRhZ2VzIDwtIHRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSAqIDEwMCAKdHh0IDwtIHBhc3RlMChuYW1lcyh0YWIpLCAnXG4nLCBwcmVjZW50YWdlcywgJyUnKSAKCnBpZSh0YWIsIGxhYmVscz10eHQgLCBtYWluID0gIlBpZSBjaGFydCBvZiBQbGF0Zm9ybSIpIAoKYGBgCgpUaGlzIHBpZSBjaGFydCBpbGx1c3RyYXRlIHBsYXRmb3JtcyBvZiBnbG9iYWwgdmlkZW8gZ2FtZXMgLCBXZSBub3RpY2UgZnJvbSB0aGUgcGllIGNoYXJ0IG9mIHBsYXRmb3JtIGF0dHJpYnV0ZSB0aGF0IHJlbGVhc2luZyBhIGdhbWUgZm9yIFBTIHVzZXJzIHdpbGwgaW5jcmVhc2UgdGhlIHBvcHVsYXJpdHkgb2YgdGhlIGdhbWUgc2luY2UgaXQgaXMgdGhlIG1vc3QgY29tbW9uIHBsYXRmb3JtIGFtb25nIGdhbWVycy4gCgoKCgoKYGBge3J9CiMgY29sb3JpbmcgYmFycGxvdCBhbmQgYWRkaW5nIHRleHQKdGFiPC1kYXRhc2V0JEdlbnJlICU+JSB0YWJsZSgpIAoKcHJlY2VudGFnZXM8LXRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSoxMDAgCgp0eHQ8LXBhc3RlMChuYW1lcyh0YWIpLCAnXG4nLHByZWNlbnRhZ2VzLCclJykgCgpiYiA8LSBkYXRhc2V0JEdlbnJlICU+JSB0YWJsZSgpICU+JSBiYXJwbG90KGF4aXNuYW1lcz1GLCBtYWluID0gIkJhcnBsb3QgZm9yIFBvcHVsYXIgZ2VucmVzICIseWxhYj0nY291bnQnLGNvbD1jKCdwaW5rJywnYmx1ZScsJ2xpZ2h0Ymx1ZScsJ2dyZWVuJywnbGlnaHRncmVlbicsJ3JlZCcsJ29yYW5nZScsJ3JlZCcsJ2dyZXknLCd5ZWxsb3cnLCdhenVyZScsJ29saXZlZHJhYicpKSAKCnRleHQoYmIsdGFiLzIsbGFiZWxzPXR4dCxjZXg9MS41KSAKYGBgClRoaXMgYmFycGxvdCBpbGx1c3RyYXRlcyBwb3B1bGFyaXR5IG9mIGdsb2JhbCB2aWRlbyBnYW1lcyBnZW5yZXMgLEluIHRlcm1zIG9mIGdlbnJlLCBhY3Rpb24gZ2FtZXMgYXJlIHRoZSBtb3N0IHBvcHVsYXIsIGZvbGxvd2VkIGJ5IHNwb3J0cyBhbmQgbXVzaWMgZ2FtZXMuIEl0IGlzIHNhZmUgdG8gYXNzdW1lIHRoYXQgYSBoaWdoIG51bWJlciBvZiBnZW5yZXMgb2YgdGhpcyBuYXR1cmUgZXhpc3QgZHVlIHRvIHRoZWlyIHBvcHVsYXJpdHkgYW5kIHNhbGVzLgoKCgoKCmBgYHtyfQpib3hwbG90KGRhdGFzZXQkTkFfU2FsZXMgLCBtYWluPSIKQm94UGxvdCBmb3IgTkFfU2FsZXMiKQpgYGAKVGhlIGJveHBsb3Qgb2YgdGhlIE5BX1NhbGVzICAoU2FsZXMgb2YgdGhlIGdhbWUgaW4gbm9ydGggQW1lcmljYSkgYXR0cmlidXRlIGluZGljYXRlcyB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlIHRvIGVhY2ggb3RoZXIgLGFuZCB0aGVyZSBhcmUgYSBsb3Qgb2Ygb3V0bGllcnMgc2luY2UgdGhlIGRhdGFzZXQgcmVwcmVzZW50cyBhbGwgdGhlIG5vcnRoIEFtZXJpY2Egc2FsZXMgb2YgdmlkZW8gZ2FtZXMuCgpgYGB7cn0KYm94cGxvdChkYXRhc2V0JEVVX1NhbGVzLCBtYWluPSIKIEJveFBsb3QgZm9yIEVVX1NhbGVzIikKYGBgClRoZSBib3hwbG90IG9mIHRoZSBFVV9TYWxlcyAoc2FsZXMgb2YgdGhlIGdhbWUgaW4gRXVyb3BlKSBhdHRyaWJ1dGUgaW5kaWNhdGVzIHRoYXQgdGhlIHZhbHVlcyBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlciwgYW5kIHRoZXJlIGFyZSBhIGxvdCBvZiBvdXRsaWVycyBzaW5jZSB0aGUgZGF0YXNldCByZXByZXNlbnRzIGFsbCB0aGUgRXVyb3BlIHNhbGVzIG9mIHZpZGVvIGdhbWVzLgoKYGBge3J9CmJveHBsb3QoZGF0YXNldCRKUF9TYWxlcyAsIG1haW49IgogQm94UGxvdCBmb3IgSlBfU2FsZXMiKQpgYGAKVGhlIGJveHBsb3Qgb2YgdGhlIEpQX1NhbGVzIChzYWxlcyBvZiB0aGUgZ2FtZSBpbiBKYXBhbikgYXR0cmlidXRlIGluZGljYXRlcyB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlIHRvIGVhY2ggb3RoZXIsIGFuZCB0aGVyZSBhcmUgYSBsb3Qgb2Ygb3V0bGllcnMgc2luY2UgdGhlIGRhdGFzZXQgcmVwcmVzZW50cyBhbGwgdGhlIEphcGFuIHNhbGVzIG9mIHZpZGVvIGdhbWVzLgoKCmBgYHtyfQpib3hwbG90KGRhdGFzZXQkT3RoZXJfU2FsZXMgLCBtYWluPSIKIEJveFBsb3QgZm9yIE90aGVyX1NhbGVzIikgCmBgYCAgCgpUaGUgYm94cGxvdCBvZiB0aGUgT3RoZXItc2FsZXMgYXR0cmlidXRlIGluZGljYXRlIHRoYXQgdGhlIHZhbHVlcyBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlciAsYW5kIHRoZXJlIGlzIGEgbG90IG9mIG91dGxpZXJzIHNpbmNlIHRoZSBkYXRhc2V0IHJlcHJlc2VudHMgdGhlIGdsb2JhbCBzYWxlcyBvZiB2aWRlbyBnYW1lcy4gCgoKCgpgYGB7cn0KYm94cGxvdChkYXRhc2V0JEdsb2JhbF9TYWxlcyAsIG1haW49IkJveFBsb3QgZm9yIEdsb2JhbF9TYWxlcyIpCgpgYGAgIApUaGUgYm94cGxvdCBvZiB0aGUgR2xvYmFsLXNhbGVzIGF0dHJpYnV0ZSBpbmRpY2F0ZSB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlIHRvIGVhY2ggb3RoZXIgLGFuZCB0aGVyZSBpcyBhIGxvdCBvZiBvdXRsaWVycyBzaW5jZSB0aGUgZGF0YXNldCByZXByZXNlbnRzIHRoZSBnbG9iYWwgc2FsZXMgb2YgdmlkZW8gZ2FtZXMuIAoKCgpgYGB7cn0KcXBsb3QoZGF0YSA9IGRhdGFzZXQsIHg9R2xvYmFsX1NhbGVzLHk9R2VucmUsZmlsbD1JKCJ5ZWxsb3ciKSx3aWR0aD0wLjUgLGdlb20gPSAiYm94cGxvdCIgLCBtYWluID0gIkJveFBsb3RzIGZvciBnZW5yZSBhbmQgR2xvYmFsX1NhbGVzIikKYGBgCgpJbiB0aGUgYm94cGxvdCB3ZSBjYW4gc2VlIHRoYXQgYWxsIHRoZSBnZW5yZXMgaGF2ZSBHbG9iXyBzYWxlcyBjbG9zZSB0byBlYWNoIG90aGVyLCBidXQgd2Ugbm90aWNlIGFuIG91dGxpZXIgdGhhdCByZWFjaGVzIG1vcmUgdGhhbiA4MCBHbG9iXyBzYWxlcyB3aGljaCBpcyBhIGdhbWUgd2l0aCBnZW5yZSBzcG9ydHMuIAoKYGBge3J9CmRhdGFzZXQkWWVhciAlPiUgdGFibGUoKSAlPiUgYmFycGxvdCggbWFpbiA9ICJCYXJwbG90IGZvciB5ZWFyIikKYGBgCgogVGhlIGJhcnBsb3Qgb2YgeWVhciBpbGx1c3RyYXRlIHRoYXQgdGhlIG51bWJlciBvZiB2aWRlbyBnYW1lcyB3ZXJlIGxvdyBmcm9tIDE5ODAgdW50aWwgMjAwMCAsIHRoZW4gbnVtYmVyIG9mIGdhbWVzIGluY3JlYXNlZCB0byBtb3JlIHRoYW4gMTIwMCB0aWxsIDIwMTIuCgoKYGBge3J9CnBhaXJzKH5OQV9TYWxlcyArIEVVX1NhbGVzICsgSlBfU2FsZXMgKyBPdGhlcl9TYWxlcyArIEdsb2JhbF9TYWxlcywgZGF0YSA9IGRhdGFzZXQsCiAgICAgIG1haW4gPSAiU2FsZXMgU2NhdHRlcnBsb3QiKQpgYGAgICAgCldlIHVzZWQgU2NhdHRlcnBsb3QgdG8gZGV0ZXJtaW5lIHRoZSB0eXBlIG9mIGNvcnJlbGF0aW9uIHdlIGhhdmUgYmV0d2VlbiB0aGUgc2FsZXM7IHdlIGNhbiBzZWUgdGhhdCB0aGUgbWFqb3JpdHkgaGF2ZSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB3aXRoIGVhY2ggb3RoZXIuIAogCiAKICAgICAgCiMgKFByZSAtIHByb2Nlc3NpbmcpOgoKIyBWYXJhaWJsZSB0cmFuc2Zvcm1hdGlvbjoKYGBge3J9CmRhdGFzZXQkUmFuaz1hcy5jaGFyYWN0ZXIoZGF0YXNldCRSYW5rKQpgYGAKV2UgdHJhbnNmb3JtZWQgdGhlIFJhbmsgZnJvbSBudW1yaWMgdG8gY2hhcixiZWNhdXNlIHdlIHdpbGwgdXNlIHRoZW0gYXMgb3JkaW5hbCBkYXRhLgoKIyBOdWxsIGNoZWNraW5nOgp3ZSBjaGVja2VkIG51bGxzIHZhbHVlcyB0byBrbm93IGhvdyBtYW55IG51bGxzIHZhbHVlcyB3ZSBoYXZlLCBzbyB3ZSBjYW4gZGV0ZXJtaW5lIGhvdyB3ZSB3aWxsIGRlYWwgd2l0aCB0aGVtLgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkUmFuaykpCk51bGxSYW5rPC1kYXRhc2V0W2RhdGFzZXQkUmFuaz09Ik4vQSIsXQpOdWxsUmFuawpgYGAKY2hlY2tpbmcgZm9yIG51bGxzIGluIFJhbmsgKHRoZXJlIGlzIG5vIG51bGxzKQpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkTmFtZSkpCk51bGxOYW1lPC1kYXRhc2V0W2RhdGFzZXQkTmFtZT09Ik4vQSIsXQpOdWxsTmFtZQpgYGAKCmNoZWNraW5nIGZvciBudWxscyBpbiBuYW1lICh0aGVyZSBpcyBubyBudWxscykKCmBgYHtyfQpzdW0oaXMubmEoZGF0YXNldCRQbGF0Zm9ybSkpCk51bGxQbGF0Zm9ybTwtZGF0YXNldFtkYXRhc2V0JFBsYXRmb3JtPT0iTi9BIixdCgoKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBQbGF0Zm9ybSh0aGVyZSBpcyBubyBudWxscykKCmBgYHtyfQpzdW0oaXMubmEoZGF0YXNldCRZZWFyKSkKTnVsbFllYXI8LWRhdGFzZXRbZGF0YXNldCRZZWFyPT0iTi9BIixdCk51bGxZZWFyCmBgYApjaGVja2luZyBmb3IgbnVsbHMgaW4geWVhcgp3ZSB3b24ndCBkZWxldGUgdGhlIG51bGwgYW5kIHdlIHdpbGwgbGVhdmUgdGhlbSBhcyBnbG9iYWwgY29uc3RhbnQgYmVjYXVzZSB3ZSB3YW50IHRoZSBzYWxlcyBkYXRhIG91dCBvZiB0aGVtLgoKYGBge3J9CnN1bShpcy5uYShkYXRhc2V0JEdlbnJlKSkKTnVsbEdlbnJlPC1kYXRhc2V0W2RhdGFzZXQkR2VucmU9PSJOL0EiLF0KTnVsbEdlbnJlCmBgYApjaGVja2luZyBmb3IgbnVsbHMgaW4gR2VucmUodGhlcmUgaXMgbm8gbnVsbHMpCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkUHVibGlzaGVyKSkKTnVsbFB1Ymxpc2hlcjwtZGF0YXNldFtkYXRhc2V0JFB1Ymxpc2hlcj09Ik4vQSIsXQpOdWxsUHVibGlzaGVyCmBgYApjaGVja2luZyBmb3IgbnVsbHMgaW4gUHVibGlzaGVyLgp3ZSB3b24ndCBkZWxldGUgdGhlIG51bGwgYW5kIHdlIHdpbGwgbGVhdmUgdGhlbSBhcyBnbG9iYWwgY29uc3RhbnQgYXMgaXQgaXMgYmVjYXVzZSB3ZSB3YW50IHRoZSBzYWxlcyBkYXRhIG9mIHRoZW0uCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkTkFfU2FsZXMpKQpOdWxsTkFfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCROQV9TYWxlcz09Ik4vQSIsXQpOdWxsTkFfU2FsZXMKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBOQV9TYWxlcyAodGhlcmUgaXMgbm8gbnVsbHMpCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkRVVfU2FsZXMpKQpOdWxsRVVfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCRFVV9TYWxlcz09Ik4vQSIsXQpOdWxsRVVfU2FsZXMKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBFVV9TYWxlcyAodGhlcmUgaXMgbm8gbnVsbHMpCgpgYGB7cn0Kc3VtKGlzLm5hKGRhdGFzZXQkSlBfU2FsZXMpKQpOdWxsSlBfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCRKUF9TYWxlcz09Ik4vQSIsXQpOdWxsSlBfU2FsZXMKYGBgCmNoZWNraW5nIGZvciBudWxscyBpbiBKUF9TYWxlcyAodGhlcmUgaXMgbm8gbnVsbHMpCgoKYGBge3J9CnN1bShpcy5uYShkYXRhc2V0JE90aGVyX1NhbGVzKSkKTnVsbE90aGVyX1NhbGVzPC1kYXRhc2V0W2RhdGFzZXQkT3RoZXJfU2FsZXM9PSJOL0EiLF0KCgpgYGAKVGhlcmUgaXMgbm8gbnVsbCB2YWx1ZXMgaW4gdGhlIG90aGVyX3NhbGVzLgoKYGBge3J9CnN1bShpcy5uYShkYXRhc2V0JEdsb2JhbF9TYWxlcykpCk51bGxHbG9iYWxfU2FsZXM8LWRhdGFzZXRbZGF0YXNldCRHbG9iYWxfU2FsZXNzPT0iTi9BIixdCgoKYGBgClRoZXJlIGlzIG5vIG51bGwgdmFsdWVzIGluIHRoZSBHbG9iYWxfU2FsZXMuCgojIEVuY29kaW5nOgpXZSB3aWxsIGVuY29kZSBvdXIgY2F0ZWdvcmljYWwgZGF0YSBzaW5jZSBtb3N0IG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyB3b3JrIHdpdGggbnVtYmVycyByYXRoZXIgdGhhbiB0ZXh0LgoKYGBge3J9CmRhdGFzZXQkUGxhdGZvcm09ZmFjdG9yKGRhdGFzZXQkUGxhdGZvcm0sbGV2ZWxzPWMoIjI2MDAiLCIzRE8iLCIzRFMiLCJEQyIsIkRTIiwiR0IiLCJHQkEiLCJHQyIsIkdFTiIsIkdHIiwiTjY0IiwiTkVTIiwiTkciLCJQQyIsIlBDRlgiLCJQUyIsIlBTMiIsIlBTMyIsIlBTNCIsIlBTUCIsIlBTViIsIlNBVCIsIlNDRCIsIlNORVMiLCJURzE2IiwiV2lpIiwiV2lpVSIsIldTIiwiWDM2MCIsIlhCIiwiWE9uZSIpLCBsYWJlbHM9YygxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwxOSwyMCwyMSwyMiwyMywyNCwyNSwyNiwyNywyOCwyOSwzMCwzMSkpCmBgYAp0aGlzIGNvbHVtbiB3aWxsIGJlIGVuY29kZWQgdG8gZmFjaWxpdGF0ZSBvdXIgZGF0YSBtaW5pbmcgdGFzay4KCmBgYHtyfQpkYXRhc2V0JEdlbnJlPWZhY3RvcihkYXRhc2V0JEdlbnJlLGxldmVscz1jKCJBY3Rpb24iLCJBZHZlbnR1cmUiLCJGaWdodGluZyIsIlBsYXRmb3JtIiwiUHV6emxlIiwiUmFjaW5nIiwiUm9sZS1QbGF5aW5nIiwiU2hvb3RlciIsIlNpbXVsYXRpb24iLCJTcG9ydHMiLCJTdHJhdGVneSIsIk1pc2MiKSxsYWJlbHM9YygxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMikpCmBgYApTaW5jZSBtb3N0IG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyB3b3JrIHdpdGggbnVtYmVycyBhbmQgbm90IHdpdGggdGV4dCBvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIHRoaXMgY29sdW1uIHdpbGwgYmUgZW5jb2RlZCB0byBmYWNpbGl0YXRlIG91ciBkYXRhIG1pbmluZyB0YXNrLgoKIyBPdXRsaWVyczoKQW5hbHlzZXMgYW5kIHN0YXRpc3RpY2FsIG1vZGVscyBjYW4gYmUgcnVpbmVkIGJ5IG91dGxpZXJzLCBtYWtpbmcgaXQgZGlmZmljdWx0IHRvIGRldGVjdCBhIHRydWUgZWZmZWN0LiBUaGVyZWZvcmUsIHdlIGFyZSBjaGVja2luZyBmb3IgdGhlbSBhbmQgcmVtb3ZpbmcgdGhlbSBpZiB3ZSBmaW5kIGFueS4KCm91dGxpZXIgb2YgTkFfU2FsZXMKYGBge3J9Ck91dE5BX1NhbGVzID0gb3V0bGllcihkYXRhc2V0JE5BX1NhbGVzLCBsb2dpY2FsID1UUlVFKQpzdW0oT3V0TkFfU2FsZXMpCkZpbmRfb3V0bGllciA9IHdoaWNoKE91dE5BX1NhbGVzID09VFJVRSwgYXJyLmluZCA9IFRSVUUpCgpgYGAKb3V0bGllciBvZiBFVV9TYWxlcwpgYGB7cn0KT3V0RVVfU2FsZXMgPSBvdXRsaWVyKGRhdGFzZXQkRVVfU2FsZXMsIGxvZ2ljYWwgPVRSVUUpCnN1bShPdXRFVV9TYWxlcykKRmluZF9vdXRsaWVyID0gd2hpY2goT3V0RVVfU2FsZXMgPT1UUlVFLCBhcnIuaW5kID0gVFJVRSkKYGBgCm91dGxpZXIgb2YgSlBfU2FsZXMKYGBge3J9Ck91dEpQX1NhbGVzID0gb3V0bGllcihkYXRhc2V0JEpQX1NhbGVzLCBsb2dpY2FsID1UUlVFKQpzdW0oT3V0SlBfU2FsZXMpCkZpbmRfb3V0bGllciA9IHdoaWNoKE91dEpQX1NhbGVzID09VFJVRSwgYXJyLmluZCA9IFRSVUUpCmBgYAoKb3V0bGllciBvZiBvdGhlcl9zYWxlcyAKYGBge3J9Ck91dE9TPW91dGxpZXIoZGF0YXNldCRPdGhlcl9TYWxlcywgbG9naWNhbD1UUlVFKSAgCnN1bShPdXRPUykgIApGaW5kX291dGxpZXI9d2hpY2goT3V0T1M9PVRSVUUsIGFyci5pbmQ9VFJVRSkgIAoKYGBgCgoKb3V0bGllciBvZiBHbG9iYWxfc2FsZXMgCgpgYGB7cn0KT3V0R1M9b3V0bGllcihkYXRhc2V0JEdsb2JhbF9TYWxlcywgbG9naWNhbD1UUlVFKSAgCnN1bShPdXRHUykgIApGaW5kX291dGxpZXI9d2hpY2goT3V0R1M9PVRSVUUsIGFyci5pbmQ9VFJVRSkgIAoKYGBgCgoKCiMgUmVtb3ZlIG91dGxpZXJzIApgYGB7cn0KZGF0YXNldD0gZGF0YXNldFstRmluZF9vdXRsaWVyLF0KYGBgCgoKCiMgTm9ybWFsaXphdGlvbjoKVGhlIG5vcm1hbGl6YXRpb24gb2YgZGF0YSB3aWxsIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlIG9mIG1hbnkgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zIGJ5IGFjY291bnRpbmcgZm9yIGRpZmZlcmVuY2VzIGluIHRoZSBzY2FsZSBvZiB0aGUgaW5wdXQgZmVhdHVyZXMuCgpEYXRhc2V0IGJlZm9yZSBub3JtYWxpemF0aW9uOgpgYGB7cn0KZGF0c2V0V2l0aG91dE5vcm1hbGl6YXRpb248LWRhdGFzZXQKYGBgCgoKYGBge3J9Cm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7cmV0dXJuICgoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSl9CmRhdGFzZXQkTkFfU2FsZXM8LW5vcm1hbGl6ZShkYXRzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiROQV9TYWxlcykKZGF0YXNldCRFVV9TYWxlczwtbm9ybWFsaXplKGRhdHNldFdpdGhvdXROb3JtYWxpemF0aW9uJEVVX1NhbGVzKQpkYXRhc2V0JEpQX1NhbGVzPC1ub3JtYWxpemUoZGF0c2V0V2l0aG91dE5vcm1hbGl6YXRpb24kSlBfU2FsZXMpCmRhdGFzZXQkT3RoZXJfU2FsZXM8LW5vcm1hbGl6ZShkYXRzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiRPdGhlcl9TYWxlcykKZGF0YXNldCRHbG9iYWxfU2FsZXM8LW5vcm1hbGl6ZShkYXRzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiRHbG9iYWxfU2FsZXMpCmBgYApXZSBjaG9zZSBtaW4tbWF4IG5vcm1hbGl6YXRpb24gaW5zdGVhZCBvZiB6LXNjb3JlIG5vcm1hbGl6YXRpb24gYmVjYXVzZSBtaW4tbWF4IHRyYW5zZm9ybSB0aGUgZGF0YSBpbnRvIGEgc3BlY2lmaWMgcmFuZ2UsIHdoaWNoIGVuaGFuY2VzIGl0cyBzdWl0YWJpbGl0eSBmb3IgdmlzdWFsaXphdGlvbiBhbmQgY29tcGFyaXNvbi4gQWRkaXRpb25hbGx5LCBpdCBzaW1wbGlmaWVzIHRoZSBwcm9jZXNzIG9mIGFzc2Vzc2luZyBhdHRyaWJ1dGUgaW1wb3J0YW5jZSBhbmQgdGhlaXIgY29udHJpYnV0aW9ucyB0byB0aGUgbW9kZWwuCgoKCgoKIyBGZWF1dHJlIHNlbGVjdGlvbjoKCk91ciBjbGFzcyBsYWJlbCAocG9wdWxhcikgcmVmZXJzIHRvIEdsb2JhbF9TYWxlcy5iZWNhdXNlIHdlIGhhdmUgbXVsdGlwbGUgcmVnaW9ucyBzYWxlcyB3ZSBjaG9zZSB0byBldmFsdWF0ZSBlYWNoIHJlZ2lvbiBzYWxlcyBiYXNlZCBvbiB0aGVpciBpbXBvcnRhbmNlIHRvIChnbG9iYWxfc2FsZXMpIGNvbHVtbixhbmQgdGhvc2UgdGhhdCBhcmUgbGVzcyBpbXBvcnRhbnQgd2lsbCBiZSBkZWxldGVkIGZyb20gdGhlIGRhdGFzZXQuCgoKVXNlIHJvY19jdXJ2ZSBhcmVhIGFzIHNjb3JlCmBgYHtyfQpyb2NfaW1wIDwtIGZpbHRlclZhckltcCh4ID0gZGF0YXNldFssNzoxMF0sIHkgPSBkYXRhc2V0JEdsb2JhbF9TYWxlcykKYGBgCgoKU29ydCB0aGUgc2NvcmUgaW4gZGVjcmVhc2luZyBvcmRlcgpgYGB7cn0Kcm9jX2ltcCA8LSBkYXRhLmZyYW1lKGNiaW5kKHZhcmlhYmxlID0gcm93bmFtZXMocm9jX2ltcCksIHNjb3JlID0gcm9jX2ltcFssMV0pKQpyb2NfaW1wJHNjb3JlIDwtIGFzLmRvdWJsZShyb2NfaW1wJHNjb3JlKQpyb2NfaW1wW29yZGVyKHJvY19pbXAkc2NvcmUsZGVjcmVhc2luZyA9IFRSVUUpLF0KYGBgCgoKd2Ugd2lsbCByZW1vdmUgdGhlIChKUF9TYWxlcykgYmVjYXVzZSBpdCBpcyBvZiBsb3cgaW1wb3J0YW5jZSB0byBvdXIgY2xhc3NfbGFiZWwoR2xvYmFsX1NhbGVzKQpgYGB7cn0KZGF0YXNldDwtIGRhdGFzZXRbLC05XQpgYGAKIyBEYXRhc2V0IGJhbG5jaW5nIGFuZCBkaXNjcml0aXphdGlvbjoKYGBge3J9CiNEaXNjcml0aXphdGlvbgoKZGF0YUJlZm9yRGlzY2VydGl6ZT0oZGF0YXNldFssNzoxMF0pCmxpYnJhcnkoImFydWxlcyIpCmRhdGFBZnRlckRpc2NlcnRpemU9ZGlzY3JldGl6ZURGKApkYXRhQmVmb3JEaXNjZXJ0aXplKQp1bmlxdWUoZGF0YUFmdGVyRGlzY2VydGl6ZVssMV0pCnVuaXF1ZShkYXRhQWZ0ZXJEaXNjZXJ0aXplWywyXSkKdW5pcXVlKGRhdGFBZnRlckRpc2NlcnRpemVbLDNdKQp1bmlxdWUoZGF0YUFmdGVyRGlzY2VydGl6ZVssNF0pCmxldmVscyhkYXRhQWZ0ZXJEaXNjZXJ0aXplJE5BX1NhbGVzKTwtYygibG93IiwibWVkaXVtIiwiaGlnaCIpCmxldmVscyhkYXRhQWZ0ZXJEaXNjZXJ0aXplJEVVX1NhbGVzKTwtYygibG93IiwiaGlnaCIpCmxldmVscyhkYXRhQWZ0ZXJEaXNjZXJ0aXplJE90aGVyX1NhbGVzKTwtYygibG93IiwiaGlnaCIpCmxldmVscyhkYXRhQWZ0ZXJEaXNjZXJ0aXplJEdsb2JhbF9TYWxlcyk8LWMoImxvdyIsIm1lZGl1bSIsImhpZ2giKQoKI0JhbGFuY2luZwoKbGlicmFyeShncm91cGRhdGEyKQpkYXRhc2V0PC1kb3duc2FtcGxlKGRhdGFzZXQsY2F0X2NvbD0iR2xvYmFsX1NhbGVzIikKCgpgYGAKCgojIERhdGFzZXQgYWZ0ZXIgcHJlLXByb2Nlc3Npbmc6CmBgYHtyfQpwcmludChkYXRhc2V0KQpgYGAKCldlIHBlcmZvcm1lZCBiYWxhbmNpbmcgYW5kIGRpc2NyaXRpemF0aW9uIGJlY2F1c2Ugd2Ugbm90aWNlIGZyb20gdGhlIGdyYXBocyB0aGF0IG91ciBkYXRhc2V0IGlzIGluYmFsYW5jZWQuCgoKCgojIERhdGEgTWluaW5nIFRlY2huaXF1ZXM6CgojIENsYXNzaWZpY2F0aW9uOgoKVGhlIGdvYWwgb2YgY2xhc3NpZmljYXRpb24gaXMgdG8gYnVpbGQgYSBtb2RlbCBvciBhbGdvcml0aG0gdGhhdCBjYW4gZ2VuZXJhbGl6ZSBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyBvYnNlcnZlZCBpbiB0aGUgdHJhaW5pbmcgZGF0YSB0byBtYWtlIGFjY3VyYXRlIHByZWRpY3Rpb25zIG9uIHVuc2VlbiBkYXRhLiBUaGUgbW9kZWwgbGVhcm5zIGZyb20gdGhlIGxhYmVsZWQgZXhhbXBsZXMgaW4gdGhlIHRyYWluaW5nIHNldCwgd2hlcmUgZWFjaCBleGFtcGxlIGNvbnNpc3RzIG9mIGEgc2V0IG9mIGlucHV0IGZlYXR1cmVzIGFuZCBhIGNvcnJlc3BvbmRpbmcga25vd24gY2xhc3MgbGFiZWwuCgojIEluZm9ybWF0aW9uIGdhaW46IAoKQnkgdXNpbmcgSW5mb3JtYXRpb24gR2FpbiB0byBzZWxlY3QgYXR0cmlidXRlcyBmb3Igc3BsaXR0aW5nLCB0aGUgZGVjaXNpb24gdHJlZSBhbGdvcml0aG0gYWltcyB0byBjcmVhdGUgYSB0cmVlIHRoYXQgbWF4aW1pemVzIHRoZSBpbmZvcm1hdGlvbiBwcm92aWRlZCBieSB0aGUgYXR0cmlidXRlcyBhYm91dCB0aGUgY2xhc3MgbGFiZWxzLCBsZWFkaW5nIHRvIGVmZmVjdGl2ZSBjbGFzc2lmaWNhdGlvbi4KCgoKCgojIENsdXN0cmluZzoKCkNsdXN0ZXJpbmcgaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBncm91cCBzaW1pbGFyIGRhdGEgcG9pbnRzIHRvZ2V0aGVyIGJhc2VkIG9uIHRoZWlyIGluaGVyZW50IGNoYXJhY3RlcmlzdGljcyBvciBzaW1pbGFyaXRpZXMuCiBTbyBvdXIgZ29hbCBvZiBjbHVzdGVyaW5nIGlzIHRvIGlkZW50aWZ5IHBhdHRlcm5zLCBzdHJ1Y3R1cmVzLCBvciByZWxhdGlvbnNoaXBzIHdpdGhpbiAKYSBkYXRhc2V0IHdpdGhvdXQgYW55IHByaW9yIGtub3dsZWRnZSBvZiB0aGUgZ3JvdXBzIG9yIGNsYXNzZXMgdGhhdCBtYXkgZXhpc3QuCgoKIyAxOlByZXByb3NzZW5pbmcgYmVmb3JlIGNsdXN0cmluZyAKCmJlZm9yZSBzdGFydGluZyB0aGUgY2x1c3RyaW5nIHByb2Nlc3Mgd2UgbmVlZCB0byByZW1vdmUgdGhlIGNsYXNzIGxhYmVsIHNpbmNlIGNsdXN0cmluZyBpcyBhbiB1bnN1cGVydmlzZWQgbGVhcm5pbmcgLCBidXQgYmVmb3JlIHJlbW92aW5nIHRoZSBjbGFzcyBsYWJlbCBXZSBzdG9yZWQgaXQgaW4gYSB2YXJpYmxlIGp1c3QgaW4gY2FzZSBvZiBmdXJ0aGVyIG5lZWQoV2UgbmVlZCBpdCB0byBjb21wdXRlIEJjdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbCksVGhlbiB3ZSBuZWVkIHRvIHRyYW5zZm9ybSBlYWNoIGZhY3RvciBjb3Vsbm0gdG8gbnVtZXJpYyBiZWNhdXNlIGl0J3MgZXNzZW50aWFsIHRvIGNvbnZlcnQgZmFjdG9yIHZhcmlhYmxlcyB0byBudW1lcmljIG9uZXMgZHVlIHRvIHRoZSBhbGdvcml0aG1pYyByZXF1aXJlbWVudHMgb2YgY2x1c3RyaW5nKEttZWFucykgYW5kIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgZmFjdG9yIHZhcmlhYmxlcy4KCmBgYHtyfQojIFdlIHN0b3JlZCB0aGUgY2xhc3MgbGFiZWwgaW4gYSB2YXJpYmxlIGp1c3QgaW4gY2FzZSBvZiBmdXJ0aGVyIG5lZWQoV2UgbmVlZCBpdCB0byBjb21wdXRlIEJjdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbCkKCmNsYXNzTGFiZWw8LWRhdGFzZXQkR2xvYmFsX1NhbGVzCgpgYGAKCgpgYGB7cn0KIyBSZW1vdmluZyB0aGUgY2xhc3NMYWJlbCBiZWZvcmUgdGhlIGNsdXN0cmluZyBwcm9jZXNzCmRhdGFzZXQ8LSBkYXRhc2V0WywtMTBdCmBgYAoKCmBgYHtyfQojIFdlIHJlbW92ZWQgY29sdW1ucyB0aGF0IGFyZSBub3QgcmVsZXZhbnQgdG8gdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcyBhbmQgY2FuIGRpc3RvcnQgdGhlIHJlc3VsdCAKZGF0YXNldENsdXN0ZXJpbmcgPC0gZGF0YXNldFssIHNldGRpZmYoMzo5LCBjKDQsIDYpKV0KVmlldyhkYXRhc2V0Q2x1c3RlcmluZykKYGBgCgoKYGBge3J9CgojI2NvbnZlcnRpbmcgZmFjdG9ycyB0byBudW1yaWMgdG8gYXBwbHkga21lYW5zIG1ldGhvZCAsIGl0J3MgZXNzZW50aWFsIHRvIGNvbnZlcnQgZmFjdG9yIHZhcmlhYmxlcyB0byBudW1lcmljIG9uZXMgZHVlIHRvIHRoZSBhbGdvcml0aG1pYyByZXF1aXJlbWVudHMgb2YgSy1tZWFucyBhbmQgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiBmYWN0b3IgdmFyaWFibGVzLgoKZGF0YXNldENsdXN0ZXJpbmckUGxhdGZvcm0gPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YXNldENsdXN0ZXJpbmckUGxhdGZvcm0pKQpkYXRhc2V0Q2x1c3RlcmluZyRHZW5yZSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhc2V0Q2x1c3RlcmluZyRHZW5yZSkpClZpZXcoZGF0YXNldENsdXN0ZXJpbmcpCmBgYAoKQWZ0ZXIgcHJlcHJvY2Vzc2luZyB0aGUgZGF0YSBub3cgd2Ugd2lsbCBzdGFydCBwZXJmb3JtaW4gdGhlIGNsdXN0cmluZyB0ZWNobmlxdWUgb24gdGhlIHByb2Nlc3NzZWQgZGF0YXNldC4KCgoKCiMgMjpLbWVhbnMKCldlIGNob3NlIEstbWVhbnMgY2x1c3RlcmluZyBhcyBvdXIgY2x1c3RyaW5nIG1ldGhvZCBiZWNhdXNlIGl0IGV4Y2VscyBpbiBoYW5kbGluZyBsYXJnZSBkYXRhc2V0cywgb2ZmZXJpbmcgcHJvbXB0IGFuZCBlYXNpbHkgdW5kZXJzdGFuZGFibGUgaW5zaWdodHMuIEl0IGlzIGJlbmVmaWNpYWwgZm9yIGV4cGxvcmluZyBkYXRhLCBmYWNpbGl0YXRpbmcgdGhlIHF1aWNrIGRldGVjdGlvbiBvZiBwb3RlbnRpYWwgZGF0YSBjbHVzdGVycy4KCiMgMzpDaG9vc2luZyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIEsKCiMgMS1TaWxob3VldHRlIG1ldGhvZApUaGlzIGdyYXBoIGRlcGljdHMgdGhlIHByb2Nlc3Mgb2YgZmluZGluZyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgZm9yIGEgZGF0YXNldCB1c2luZyB0aGUgU2lsaG91ZXR0ZSBtZXRob2QuIFRoZSB4LWF4aXMgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIChrKSBjb25zaWRlcmVkIGluIHRoZSBhbmFseXNpcywgcmFuZ2luZyBmcm9tIDEgdG8gMTAuIFRoZSB5LWF4aXMgc2hvd3MgdGhlIGF2ZXJhZ2UgU2lsaG91ZXR0ZSB3aWR0aCwgd2hpY2ggaXMgYSBtZWFzdXJlIG9mIGhvdyBzaW1pbGFyIGFuIG9iamVjdCBpcyB0byBpdHMgb3duIGNsdXN0ZXIgY29tcGFyZWQgdG8gb3RoZXIgY2x1c3RlcnMuCgpgYGB7cn0KZnZpel9uYmNsdXN0KGRhdGFzZXRDbHVzdGVyaW5nLCBrbWVhbnMsIG1ldGhvZCA9ICJzaWxob3VldHRlIikrCiAgbGFicyhzdWJ0aXRsZSA9ICJTaWxob3VldHRlIG1ldGhvZCIpCmBgYAoKVGhlIHBsb3Qgc2hvd3MgYSBwZWFrIGF0IGs9Mywgd2hlcmUgdGhlIGF2ZXJhZ2UgU2lsaG91ZXR0ZSBzY29yZSBpcyB0aGUgaGlnaGVzdC4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBkYXRhIHBvaW50cyBhcmUsIG9uIGF2ZXJhZ2UsIGNsb3NlciB0byBvdGhlciBwb2ludHMgaW4gdGhlaXIgb3duIGNsdXN0ZXIgYW5kIGZhcnRoZXIgZnJvbSBwb2ludHMgaW4gb3RoZXIgY2x1c3RlcnMgd2hlbiB0aGUgZGF0YSBpcyBkaXZpZGVkIGludG8gdGhyZWUgY2x1c3RlcnMuIEFzIGEgcmVzdWx0LCBhY2NvcmRpbmcgdG8gdGhlIFNpbGhvdWV0dGUgbWV0aG9kLCBrPTMgaXMgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzLgoKIyAyLSBFbGJvdyBtZXRob2QKVGhlIEVsYm93IE1ldGhvZCB1c2luZyBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlcyAoV1NTKSBpcyBhIHRlY2huaXF1ZSB0byBkZXRlcm1pbmUgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGluIEstbWVhbnMgY2x1c3RlcmluZy4gSXQgaW52b2x2ZXMgcnVubmluZyB0aGUgY2x1c3RlcmluZyBhbGdvcml0aG0gZm9yIGEgcmFuZ2Ugb2YgY2x1c3RlciBudW1iZXJzIGFuZCBjYWxjdWxhdGluZyB0aGUgV1NTIGZvciBlYWNoLiBXU1MgaXMgdGhlIHN1bSBvZiBzcXVhcmVkIGRpc3RhbmNlcyBvZiBlYWNoIHBvaW50IHRvIGl0cyBjbHVzdGVyIGNlbnRyb2lkLiBBcyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGluY3JlYXNlcywgV1NTIHRlbmRzIHRvIGRlY3JlYXNlOyB0aGUgZ29hbCBpcyB0byBmaW5kIHRoZSBwb2ludCB3aGVyZSBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgZG9lcyBub3QgbGVhZCB0byBhIHNpZ25pZmljYW50IGRlY3JlYXNlIGluIFdTUy4gVGhpcyBwb2ludCwgdmlzdWFsbHkgcmVzZW1ibGluZyBhbiBlbGJvdyBvbiBhIHBsb3Qgb2YgV1NTIGFnYWluc3QgdGhlIG51bWJlciBvZiBjbHVzdGVycywgaXMgY29uc2lkZXJlZCB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMuCmBgYHtyfQpmdml6X25iY2x1c3QoZGF0YXNldENsdXN0ZXJpbmcsIGttZWFucywgbWV0aG9kID0gIndzcyIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBsaW5ldHlwZSA9IDIpKwogIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikKYGBgCgpBcyBzaG93biBpbiB0aGUgYWJvdmUgZ3JhcGggLCA0IGlzIHRoZSB2YWx1ZSB0aGF0IHJlc2VtYmxlcyBhbiBlbGJvdyBpbiB0aGUgcGxvdChUaGUgdHVybm5pbmcgcG9pbnQpIHdpY2ggbWVhbnMgaXQgaXMgdGhlIG9wdGltYWwgdmFsdWUgb2YgSyB0aGUgd2Ugd2lsbCB1c2UgaW4gb3VyIGNsdXN0cmluZyBwcm9jZXNzLgoKSW4gY29uY2x1c2lvbiwgd2Ugd2lsbCBjaG9vc2UgSz00IGZvciBvdXIgY2x1c3RlcmluZyBwcm9jZXNzLCBhcyBpdCBtYXJrcyB0aGUgdHVybmluZyBwb2ludCBvbiB0aGUgRWxib3cgTWV0aG9kIGN1cnZlLCBpbmRpY2F0aW5nIGFuIG9wdGltYWwgYmFsYW5jZSBpbiBjbHVzdGVyIGNvbXBhY3RuZXNzIGFuZCBzZXBhcmF0aW9uLiBBZGRpdGlvbmFsbHksIHdlIHdpbGwgdXRpbGl6ZSBLPTMgYW5kIEs9NiwgYXMgdGhlc2UgdmFsdWVzIG1heGltaXplIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGgsIHdpdGggSz0zIGJlaW5nIHRoZSBwcmltYXJ5IG1heGltaXplciBhbmQgSz02IHRoZSBzZWNvbmRhcnkuIEJ5IHNlbGVjdGluZyB0aGVzZSBzcGVjaWZpYyBLIHZhbHVlcywgd2UgYWltIHRvIGFjaGlldmUgYSBzYXRpc2ZhY3RvcnkgbGV2ZWwgb2YgcHJlY2lzaW9uIGFuZCByZWNhbGwgaW4gb3VyIGNsdXN0ZXJpbmcgYW5hbHlzaXMsIGVuc3VyaW5nIGJvdGggdGhlIHJlbGV2YW5jZSBhbmQgY29tcGxldGVuZXNzIG9mIHRoZSBjbHVzdGVyZWQgZGF0YS4KCgoKIyBrLW1lYW5zIGNsdXN0ZXJpbmcsIHZpc3VhbGl6YXRpb24gYW5kIGV2YWx1YXRpb24KCiMgMS0gaz0zIAoKYGBge3J9CnNldC5zZWVkKDUwMDApCmttZWFucy5yZXN1bHQgPC0ga21lYW5zKGRhdGFzZXRDbHVzdGVyaW5nLCAzKQoKIyBwcmludCB0aGUgY2x1c3Rlcm5nIHJlc3VsdAprbWVhbnMucmVzdWx0CmBgYAoKCgpgYGB7cn0KIyB2aXN1YWxpemUgY2x1c3RlcmluZwpsaWJyYXJ5KGZhY3RvZXh0cmEpCmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gZGF0YXNldENsdXN0ZXJpbmcpCmBgYAoKYGBge3J9CiNhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGNsdXN0ZXIgIGs9MwpsaWJyYXJ5KGNsdXN0ZXIpCmF2Z19zaWwgPC0gc2lsaG91ZXR0ZShrbWVhbnMucmVzdWx0JGNsdXN0ZXIsZGlzdChkYXRhc2V0Q2x1c3RlcmluZykpCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKQpgYGAKCmBgYHtyfQojV2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgd3NzIAp3c3MgPC0ga21lYW5zLnJlc3VsdCR0b3Qud2l0aGluc3MKcHJpbnQod3NzKQpgYGAKCmBgYHtyfQojQkN1YmVkCmttZWFuc19jbHVzdGVyIDwtIGMoa21lYW5zLnJlc3VsdCRjbHVzdGVyKQoKZ3JvdW5kX3RydXRoIDwtIGMoY2xhc3NMYWJlbCkKCmRhdGEgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0ga21lYW5zX2NsdXN0ZXIsIGxhYmVsID0gZ3JvdW5kX3RydXRoKQoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsCiAgYmN1YmVkIDwtIGZ1bmN0aW9uKGRhdGEpIHsKICBuIDwtIG5yb3coZGF0YSkKICB0b3RhbF9wcmVjZXNpb24gPC0gMAogIHRvdGFsX3JlY2FsbCA8LSAwCgogIGZvciAoaSBpbiAxOm4pIHsKICAgIGNsdXN0ZXIgPC0gZGF0YSRjbHVzdGVyW2ldCiAgICBsYWJlbCA8LSBkYXRhJGxhYmVsW2ldCiAgICAKIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgd2l0aGluIHRoZSBzYW1lIGNsdXN0ZXIKaW50ZXJzZWN0aW9uIDwtIHN1bShkYXRhJGxhYmVsW2RhdGEkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkKICAgIAojIENvdW50IHRoZSB0b3RhbCBudW1iZXIgb2YgaXRlbXMgaW4gdGhlIHNhbWUgY2x1c3Rlcgp0b3RhbF9zYW1lX2NsdXN0ZXIgPC0gc3VtKGRhdGEkY2x1c3RlciA9PSBjbHVzdGVyKQogICAgCiMgQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBpdGVtcyB3aXRoIHRoZSBzYW1lIGNhdGVnb3J5CnRvdGFsX3NhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGEkbGFiZWwgPT0gbGFiZWwpCiAgICAKIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwgZm9yIHRoZSBjdXJyZW50IGl0ZW0gYW5kIGFkZCB0aGVtIHRvIHRoZSBzdW1zCnRvdGFsX3ByZWNlc2lvbiA8LSB0b3RhbF9wcmVjZXNpb24gKyBpbnRlcnNlY3Rpb24gL3RvdGFsX3NhbWVfY2x1c3Rlcgp0b3RhbF9yZWNhbGwgPC0gdG90YWxfcmVjYWxsICsgaW50ZXJzZWN0aW9uIC8gdG90YWxfc2FtZV9jYXRlZ29yeQogIH0KCiAgIyBDYWxjdWxhdGUgYXZlcmFnZSBwcmVjaXNpb24gYW5kIHJlY2FsbAogIHByZWNpc2lvbiA8LSB0b3RhbF9wcmVjZXNpb24gLyBuCiAgcmVjYWxsIDwtIHRvdGFsX3JlY2FsbCAvIG4KCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQp9CgojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwKbWV0cmljcyA8LSBiY3ViZWQoZGF0YSkKCiMgRXh0cmFjdCBwcmVjaXNpb24gYW5kIHJlY2FsbCBmcm9tIHRoZSBtZXRyaWNzCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbgpyZWNhbGwgPC0gbWV0cmljcyRyZWNhbGwKCiMgUHJpbnQgdGhlIHJlc3VsdHMKY2F0KCJCQ3ViZWQgUHJlY2lzaW9uOiIsIHByZWNpc2lvbiwgIlxuIikKCmNhdCgiQkN1YmVkIFJlY2FsbDoiLCByZWNhbGwsICJcbiIpCmBgYAoKIEFzIHRoZSBncmFwaCBvZiBLPTMgaWxsdXN0cmF0ZWQgLCB0aGVyZSBpcyBhIG5vdGljZWFibGUgb3ZlcmxhcHBpbmcgYmV0d2VlbiB0aGUgY2x1c3RlcnMgdGhhdCBlZmZlY3QgdGhlIGNsdXN0ZXIgcGVyZm9ybWFuY2UgZHVvIHRvIHRoZSBzaW1pbGFyaXR5IGJldHdlZW4gY2x1c3RlcnMgYW5kIHdpZGUgZGlzdGFuY2UgaW4gdGhlIGNsdXN0ZXIgaXRzZWxmIGFzIHRoZSBoaWdoIHZhbHVlIG9mIHdzcyAgaW5kaWNhdGUoNzclKSAsdGhlIHJlY2FsbCB2YWx1ZSBpcyBhY2NlcHRhYmxlIHdoaWNoIGlzIDAuMzkgLCB0aGUgdmFsdWUgb2YgcHJlY2lzaW9uIDAuMDIxIGlzIGxvdyB3aGljaCBjYW4gYmUgZHVvIHRvIHRoZSBwcmVzZW5jZSBvZiBvdXRsaWVycyAsdGhlIHZhbHVlIG9mIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCBpcyAwLjUyIHJlbGF0aXZlbHkgZ29vZCBmb3IgdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcywgT3ZlcmFsbCwgdGhlIHBsb3Qgc3VnZ2VzdHMgdGhhdCBkaXZpZGluZyB0aGUgZGF0YSBpbnRvIDMgY2x1c3RlcnMgc2VlbXMgYXBwcm9wcmlhdGUgYmVjYXVzZSB0aGUgYXZlcmFnZSBTaWxob3VldHRlIHNjb3JlIGlzIHJlbGF0aXZsZXkgaGlnaC4KCgoKIyAxLSBrPTQKCmBgYHtyfQpzZXQuc2VlZCg1MDAwKQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhkYXRhc2V0Q2x1c3RlcmluZywgNCkKCiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHQKa21lYW5zLnJlc3VsdApgYGAKCgoKYGBge3J9CiMgdmlzdWFsaXplIGNsdXN0ZXJpbmcKbGlicmFyeShmYWN0b2V4dHJhKQpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IGRhdGFzZXRDbHVzdGVyaW5nKQpgYGAKCmBgYHtyfQojYXZlcmFnZSBzaWxob3VldHRlIGZvciBjbHVzdGVyICBrPTQKbGlicmFyeShjbHVzdGVyKQphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLGRpc3QoZGF0YXNldENsdXN0ZXJpbmcpKQpmdml6X3NpbGhvdWV0dGUoYXZnX3NpbCkKYGBgCgpgYGB7cn0KI1dpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIHdzcyAKd3NzIDwtIGttZWFucy5yZXN1bHQkdG90LndpdGhpbnNzCnByaW50KHdzcykKYGBgCgpgYGB7cn0KI0JDdWJlZAprbWVhbnNfY2x1c3RlciA8LSBjKGttZWFucy5yZXN1bHQkY2x1c3RlcikKCmdyb3VuZF90cnV0aCA8LSBjKGNsYXNzTGFiZWwpCgpkYXRhIDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGttZWFuc19jbHVzdGVyLCBsYWJlbCA9IGdyb3VuZF90cnV0aCkKCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbAogIGJjdWJlZCA8LSBmdW5jdGlvbihkYXRhKSB7CiAgbiA8LSBucm93KGRhdGEpCiAgdG90YWxfcHJlY2VzaW9uIDwtIDAKICB0b3RhbF9yZWNhbGwgPC0gMAoKICBmb3IgKGkgaW4gMTpuKSB7CiAgICBjbHVzdGVyIDwtIGRhdGEkY2x1c3RlcltpXQogICAgbGFiZWwgPC0gZGF0YSRsYWJlbFtpXQogICAgCiMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBmcm9tIHRoZSBzYW1lIGNhdGVnb3J5IHdpdGhpbiB0aGUgc2FtZSBjbHVzdGVyCmludGVyc2VjdGlvbiA8LSBzdW0oZGF0YSRsYWJlbFtkYXRhJGNsdXN0ZXIgPT0gY2x1c3Rlcl0gPT0gbGFiZWwpCiAgICAKIyBDb3VudCB0aGUgdG90YWwgbnVtYmVyIG9mIGl0ZW1zIGluIHRoZSBzYW1lIGNsdXN0ZXIKdG90YWxfc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhJGNsdXN0ZXIgPT0gY2x1c3RlcikKICAgIAojIENvdW50IHRoZSB0b3RhbCBudW1iZXIgb2YgaXRlbXMgd2l0aCB0aGUgc2FtZSBjYXRlZ29yeQp0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhJGxhYmVsID09IGxhYmVsKQogICAgCiMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsIGZvciB0aGUgY3VycmVudCBpdGVtIGFuZCBhZGQgdGhlbSB0byB0aGUgc3Vtcwp0b3RhbF9wcmVjZXNpb24gPC0gdG90YWxfcHJlY2VzaW9uICsgaW50ZXJzZWN0aW9uIC90b3RhbF9zYW1lX2NsdXN0ZXIKdG90YWxfcmVjYWxsIDwtIHRvdGFsX3JlY2FsbCArIGludGVyc2VjdGlvbiAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkKICB9CgogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcHJlY2lzaW9uIGFuZCByZWNhbGwKICBwcmVjaXNpb24gPC0gdG90YWxfcHJlY2VzaW9uIC8gbgogIHJlY2FsbCA8LSB0b3RhbF9yZWNhbGwgLyBuCgogIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSkKfQoKIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsCm1ldHJpY3MgPC0gYmN1YmVkKGRhdGEpCgojIEV4dHJhY3QgcHJlY2lzaW9uIGFuZCByZWNhbGwgZnJvbSB0aGUgbWV0cmljcwpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsCgojIFByaW50IHRoZSByZXN1bHRzCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpCgpjYXQoIkJDdWJlZCBSZWNhbGw6IiwgcmVjYWxsLCAiXG4iKQpgYGAKCkFzIHRoZSBncmFwaCBvZiBLPTQgaWxsdXN0cmF0ZWQgLCB0aGVyZSBpcyBhIG5vdGljZWFibGUgb3ZlcmxhcHBpbmcgYmV0d2VlbiB0aGUgY2x1c3RlcnMgdGhhdCBlZmZlY3QgdGhlIGNsdXN0ZXIgcGVyZm9ybWFuY2UgZHVvIHRvIHRoZSBzaW1pbGFyaXR5IGJldHdlZW4gY2x1c3RlcnMgYW5kIHNvbWUgd2lkZSBkaXN0YW5jZSBpbiB0aGUgY2x1c3RlciBpdHNlbGYgYXMgdGhlIGhpZ2ggdmFsdWUgb2Ygd3NzIGluZGljYXRlKDgzJSkgLHRoZSByZWNhbGwgdmFsdWUgaXMgYWNjZXB0YWJsZSB3aGljaCBpcyAwLjI4IGJ1dCBsZXNzIHRoYW4gSz0zIHJlY2FsbCB2YWx1ZSAsIHRoZSB2YWx1ZSBvZiBwcmVjaXNpb24gMC4wMjUgaXMgbG93IHdoaWNoIGNhbiBiZSBkdW8gdG8gdGhlIHByZXNlbmNlIG9mIG91dGxpZXJzICx0aGUgdmFsdWUgb2YgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIDAuNTIgd2hpY2ggaXMgcmVsYXRpdmVseSBnb29kIGZvciB0aGUgY2x1c3RlcmluZyBwcm9jZXNzLCBPdmVyYWxsLCB0aGUgcGxvdCBzdWdnZXN0cyB0aGF0IGRpdmlkaW5nIHRoZSBkYXRhIGludG8gNCBjbHVzdGVycyBzZWVtcyBhcHByb3ByaWF0ZSBidXQgaXQgd291bGQgYmV0dGVyIGlmIGNob29zZSBrPTMgYmVjYXVzZSBpdCBoYXMgbG93ZXIgd3NzIHZhbHVlIGFuZCBoaWdoZXIgcHJlY2lzaW9uIGFuZCByZWNhbGwgLgoKCiMgMS0gaz02CgpgYGB7cn0Kc2V0LnNlZWQoNTAwMCkKa21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YXNldENsdXN0ZXJpbmcsIDYpCgojIHByaW50IHRoZSBjbHVzdGVybmcgcmVzdWx0CmttZWFucy5yZXN1bHQKYGBgCgoKCmBgYHtyfQojIHZpc3VhbGl6ZSBjbHVzdGVyaW5nCmxpYnJhcnkoZmFjdG9leHRyYSkKZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhc2V0Q2x1c3RlcmluZykKYGBgCgpgYGB7cn0KI2F2ZXJhZ2Ugc2lsaG91ZXR0ZSBmb3IgY2x1c3RlciAgaz02CmxpYnJhcnkoY2x1c3RlcikKYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlcixkaXN0KGRhdGFzZXRDbHVzdGVyaW5nKSkKZnZpel9zaWxob3VldHRlKGF2Z19zaWwpCmBgYAoKYGBge3J9CiNXaXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcyB3c3MgCndzcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcwpwcmludCh3c3MpCmBgYAoKYGBge3J9CiNCQ3ViZWQKa21lYW5zX2NsdXN0ZXIgPC0gYyhrbWVhbnMucmVzdWx0JGNsdXN0ZXIpCgpncm91bmRfdHJ1dGggPC0gYyhjbGFzc0xhYmVsKQoKZGF0YSA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBrbWVhbnNfY2x1c3RlciwgbGFiZWwgPSBncm91bmRfdHJ1dGgpCgojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwKICBiY3ViZWQgPC0gZnVuY3Rpb24oZGF0YSkgewogIG4gPC0gbnJvdyhkYXRhKQogIHRvdGFsX3ByZWNlc2lvbiA8LSAwCiAgdG90YWxfcmVjYWxsIDwtIDAKCiAgZm9yIChpIGluIDE6bikgewogICAgY2x1c3RlciA8LSBkYXRhJGNsdXN0ZXJbaV0KICAgIGxhYmVsIDwtIGRhdGEkbGFiZWxbaV0KICAgIAojIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgZnJvbSB0aGUgc2FtZSBjYXRlZ29yeSB3aXRoaW4gdGhlIHNhbWUgY2x1c3RlcgppbnRlcnNlY3Rpb24gPC0gc3VtKGRhdGEkbGFiZWxbZGF0YSRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQogICAgCiMgQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBpdGVtcyBpbiB0aGUgc2FtZSBjbHVzdGVyCnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpCiAgICAKIyBDb3VudCB0aGUgdG90YWwgbnVtYmVyIG9mIGl0ZW1zIHdpdGggdGhlIHNhbWUgY2F0ZWdvcnkKdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbCkKICAgIAojIENhbGN1bGF0ZSBwcmVjaXNpb24gYW5kIHJlY2FsbCBmb3IgdGhlIGN1cnJlbnQgaXRlbSBhbmQgYWRkIHRoZW0gdG8gdGhlIHN1bXMKdG90YWxfcHJlY2VzaW9uIDwtIHRvdGFsX3ByZWNlc2lvbiArIGludGVyc2VjdGlvbiAvdG90YWxfc2FtZV9jbHVzdGVyCnRvdGFsX3JlY2FsbCA8LSB0b3RhbF9yZWNhbGwgKyBpbnRlcnNlY3Rpb24gLyB0b3RhbF9zYW1lX2NhdGVnb3J5CiAgfQoKICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsCiAgcHJlY2lzaW9uIDwtIHRvdGFsX3ByZWNlc2lvbiAvIG4KICByZWNhbGwgPC0gdG90YWxfcmVjYWxsIC8gbgoKICByZXR1cm4obGlzdChwcmVjaXNpb24gPSBwcmVjaXNpb24sIHJlY2FsbCA9IHJlY2FsbCkpCn0KCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbAptZXRyaWNzIDwtIGJjdWJlZChkYXRhKQoKIyBFeHRyYWN0IHByZWNpc2lvbiBhbmQgcmVjYWxsIGZyb20gdGhlIG1ldHJpY3MKcHJlY2lzaW9uIDwtIG1ldHJpY3MkcHJlY2lzaW9uCnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbAoKIyBQcmludCB0aGUgcmVzdWx0cwpjYXQoIkJDdWJlZCBQcmVjaXNpb246IiwgcHJlY2lzaW9uLCAiXG4iKQoKY2F0KCJCQ3ViZWQgUmVjYWxsOiIsIHJlY2FsbCwgIlxuIikKYGBgCgoKCnwgTWVhc3VyZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLPTMgICAgICAgIHwgSz00ICAgICAgICB8IEs9NiAgICAgICAgfAp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLTp8LS0tLS0tLS0tLS06fC0tLS0tLS0tLS0tOnwKfCBBdmVyYWdlIFNpbGhvdWV0dGUgd2lkdGggICAgICAgICAgICB8IDAuNTUgICAgICAgfCAwLjU0ICAgICAgIHwgMC41MiAgICAgICB8CnwgVG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZSAgfCAxMDMzMC45NSAgIHwgNzAyNC40NTMgICB8IDQ1NTkuMDU1ICAgfAp8IEJDdWJlZCBwcmVjaXNpb24gICAgICAgICAgICAgICAgICAgIHwgMC4wMDQ4MjMxNSB8IDAuMDA2NDMwODcgfCAwLjAwOTY0NjMwIHwKfCBCQ3ViZWQgcmVjYWxsICAgICAgICAgICAgICAgICAgICAgICB8IDEuMDAgICAgICAgfCAxLjAwICAgICAgIHwgMS4wMCAgICAgICB8CgoKCgpBcyB0aGUgZ3JhcGggb2YgSz02IGlsbHVzdHJhdGVkICwgdGhlcmUgaXMgYSBub3RpY2VhYmxlIG92ZXJsYXBwaW5nIGJldHdlZW4gdGhlIGNsdXN0ZXJzIHRoYXQgZWZmZWN0IHRoZSBjbHVzdGVyIHBlcmZvcm1hbmNlIGR1byB0byB0aGUgc2ltaWxhcml0eSBiZXR3ZWVuIGNsdXN0ZXJzIGFuZCB3aWRlIGRpc3RhbmNlIGluIHRoZSBjbHVzdGVyIGl0c2VsZiBhcyB0aGUgaGlnaCB2YWx1ZSBvZiB3c3MgaW5kaWNhdGUgKDg2JSkgLHRoZSByZWNhbGwgdmFsdWUgaXMgYWNjZXB0YWJsZSB3aGljaCBpcyAwLjIyIGFuZCBpdCBpcyB0aGUgbG93ZXN0IHZhbHVlIGJldHdlZW4gdGhlIG90aGVyIGNsdXN0ZXJzLCB0aGUgdmFsdWUgb2YgcHJlY2lzaW9uIDAuMDIgaXMgbG93IHdoaWNoIGNhbiBiZSBkdW8gdG8gdGhlIHByZXNlbmNlIG9mIG91dGxpZXJzLCB0aGUgdmFsdWUgb2YgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIDAuNCB3aGljaCBpcyBiYWQgZm9yIHRoZSBjbHVzdGVyaW5nIHByb2Nlc3MgaXQuIE92ZXJhbGwsIHRoZSBwbG90IHN1Z2dlc3RzIHRoYXQgZGl2aWRpbmcgdGhlIGRhdGEgaW50byAzIGNsdXN0ZXJzIHNlZW1zIGFwcHJvcHJpYXRlIGJlY2F1c2UgdGhlIGF2ZXJhZ2UgU2lsaG91ZXR0ZSBzY29yZSBpcyBoaWdoIGFuZCB0aGUgaW5kaXZpZHVhbCBkYXRhIHBvaW50cyBnZW5lcmFsbHkgaGF2ZSBnb29kIFNpbGhvdWV0dGUgdmFsdWVzLg==